diff --git a/MetaMorpheus/EngineLayer/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/BioPolymerNotchFragmentIonComparer.cs deleted file mode 100644 index 8c2b1c528d..0000000000 --- a/MetaMorpheus/EngineLayer/BioPolymerNotchFragmentIonComparer.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Omics; -using Omics.Fragmentation; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace EngineLayer -{ - public class BioPolymerNotchFragmentIonComparer : Comparer<(int notch, IBioPolymerWithSetMods pwsm, List ions)> - { - /// - /// Returns greater than 0 if x is better than y, less than 0 if y is better than x, and 0 if they are equal. - /// Better is defined as having a lower notch, more fragment ions, or a shorter sequence (i.e. fewer modifications) in that order. - /// If the aforementioned criteria are equal, then the two are compared based on the alphebetical ordering of the full sequence - /// - public override int Compare((int notch, IBioPolymerWithSetMods pwsm, List ions) x, (int notch, IBioPolymerWithSetMods pwsm, List ions) y) - { - if (x.notch != y.notch) - return -1 * x.notch.CompareTo(y.notch); // Lower notch is better - - if (x.ions?.Count != y.ions?.Count && !ReferenceEquals(x.ions, null)) - return x.ions.Count.CompareTo(y.ions?.Count); // More ions are better - - if(x.pwsm.NumMods != y.pwsm.NumMods) - return -1 * x.pwsm.NumMods.CompareTo(y.pwsm.NumMods); // Fewer mods are better - - if(x.pwsm.FullSequence != y.pwsm.FullSequence) - return -1 * String.Compare(x.pwsm.FullSequence, y.pwsm.FullSequence); // (reverse) Alphabetical ordering of full sequence - - if(x.pwsm.Parent.Accession != y.pwsm.Parent.Accession) // This will break if the protein accession is not set (I'm not sure if that's possible) - return -1 * String.Compare(x.pwsm.Parent.Accession, y.pwsm.Parent.Accession); // (reverse) Alphabetical ordering of protein accession - - return -1 * x.pwsm.OneBasedStartResidue.CompareTo(y.pwsm.OneBasedStartResidue); - } - } -} diff --git a/MetaMorpheus/EngineLayer/Calibration/DataPointAcquisitionEngine.cs b/MetaMorpheus/EngineLayer/Calibration/DataPointAcquisitionEngine.cs index 90fc0ad06e..19a0c26be7 100644 --- a/MetaMorpheus/EngineLayer/Calibration/DataPointAcquisitionEngine.cs +++ b/MetaMorpheus/EngineLayer/Calibration/DataPointAcquisitionEngine.cs @@ -71,10 +71,10 @@ protected override MetaMorpheusEngineResults RunSpecific() int ms2scanNumber = identification.ScanNumber; int peptideCharge = identification.ScanPrecursorCharge; //skip if ambiguous - if (identification.FullSequence == null || identification.BestMatchingBioPolymersWithSetMods.Any(p => p.Peptide.AllModsOneIsNterminus.Any(m => m.Value.ChemicalFormula == null))) + if (identification.FullSequence == null || identification.BestMatchingBioPolymersWithSetMods.Any(p => p.WithSetMods.AllModsOneIsNterminus.Any(m => m.Value.ChemicalFormula == null))) continue; - var representativeSinglePeptide = identification.BestMatchingBioPolymersWithSetMods.First().Peptide; + var representativeSinglePeptide = identification.BestMatchingBioPolymersWithSetMods.First().WithSetMods; // Get the peptide, don't forget to add the modifications!!!! var SequenceWithChemicalFormulas = representativeSinglePeptide.SequenceWithChemicalFormulas; diff --git a/MetaMorpheus/EngineLayer/ClassicSearch/MiniClassicSearchEngine.cs b/MetaMorpheus/EngineLayer/ClassicSearch/MiniClassicSearchEngine.cs index 80ad9eff7e..885989c151 100644 --- a/MetaMorpheus/EngineLayer/ClassicSearch/MiniClassicSearchEngine.cs +++ b/MetaMorpheus/EngineLayer/ClassicSearch/MiniClassicSearchEngine.cs @@ -3,7 +3,6 @@ using MzLibUtil; using Omics; using Omics.Fragmentation; -using Proteomics.ProteolyticDigestion; using System; using System.Collections.Generic; using System.Linq; @@ -129,18 +128,16 @@ public static void CalculateSpectralAngles(SpectralLibrary spectralLibrary, Spec if (psms[i] != null) { Ms2ScanWithSpecificMass scan = arrayOfSortedMs2Scans[psms[i].ScanIndex]; - List<(int, IBioPolymerWithSetMods)> pwsms = new(); List pwsmSpectralAngles = new(); - foreach (var (Notch, Peptide) in psms[i].BestMatchingBioPolymersWithSetMods) + foreach (var bestMatch in psms[i].BestMatchingBioPolymersWithSetMods) { //if peptide is target, directly look for the target's spectrum in the spectral library - if (!Peptide.Parent.IsDecoy && spectralLibrary.TryGetSpectrum(Peptide.FullSequence, scan.PrecursorCharge, out var librarySpectrum)) + if (!bestMatch.IsDecoy && spectralLibrary.TryGetSpectrum(bestMatch.FullSequence, scan.PrecursorCharge, out var librarySpectrum)) { SpectralSimilarity s = new SpectralSimilarity(scan.TheScan.MassSpectrum, librarySpectrum.XArray, librarySpectrum.YArray, SpectralSimilarity.SpectrumNormalizationScheme.SquareRootSpectrumSum, fileSpecificParameters.ProductMassTolerance.Value, false); if (s.SpectralContrastAngle().HasValue) { - pwsms.Add((Notch, Peptide)); pwsmSpectralAngles.Add((double)s.SpectralContrastAngle()); } } diff --git a/MetaMorpheus/EngineLayer/CrosslinkSearch/CrosslinkSpectralMatch.cs b/MetaMorpheus/EngineLayer/CrosslinkSearch/CrosslinkSpectralMatch.cs index 4bdf2522cc..033ff0f3ff 100644 --- a/MetaMorpheus/EngineLayer/CrosslinkSearch/CrosslinkSpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/CrosslinkSearch/CrosslinkSpectralMatch.cs @@ -25,7 +25,7 @@ public CrosslinkSpectralMatch( XLTotalScore = score; _BestMatchingBioPolymersWithSetMods.Clear(); - _BestMatchingBioPolymersWithSetMods.Add((0, theBestPeptide)); + _BestMatchingBioPolymersWithSetMods.Add(new SpectrumMatch.TentativeSpectralMatch(0, theBestPeptide, matchedFragmentIons)); } @@ -127,8 +127,8 @@ public static bool IsIntraCsm(CrosslinkSpectralMatch csm) if (csm.Accession == null) { - var alphaProteins = csm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide.Parent.Accession).ToList(); - var betaProteins = csm.BetaPeptide.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide.Parent.Accession).ToList(); + var alphaProteins = csm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods.Parent.Accession).ToList(); + var betaProteins = csm.BetaPeptide.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods.Parent.Accession).ToList(); foreach (var alpha in alphaProteins) { @@ -373,7 +373,7 @@ public override string ToString() } sb.Append("\t"); //Intentionally left empty for readability in the tsv file. - List pepsWithMods = BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide as PeptideWithSetModifications).ToList(); + List pepsWithMods = BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods as PeptideWithSetModifications).ToList(); var proteinAccessionString = Accession ?? PsmTsvWriter.Resolve(pepsWithMods.Select(b => b.Protein.Accession), FullSequence).ResolvedString; sb.Append(proteinAccessionString + "\t"); sb.Append(XlProteinPos + (XlProteinPosLoop.HasValue ? "~" + XlProteinPosLoop.Value : null) + "\t"); @@ -422,7 +422,7 @@ public override string ToString() if (BetaPeptide != null) { sb.Append("\t"); //Intentionally left empty for readability in the tsv file. - List betaPepsWithMods = BetaPeptide.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide as PeptideWithSetModifications).ToList(); + List betaPepsWithMods = BetaPeptide.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods as PeptideWithSetModifications).ToList(); var betaProteinAccessionString = BetaPeptide.Accession ?? PsmTsvWriter.Resolve(betaPepsWithMods.Select(b => b.Protein.Accession), FullSequence).ResolvedString; sb.Append(betaProteinAccessionString + "\t"); sb.Append(BetaPeptide.XlProteinPos + "\t"); diff --git a/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs b/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs index ac07d9174f..d6fcc5cc09 100644 --- a/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs +++ b/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs @@ -175,10 +175,10 @@ public void CalculateQValue(List psms, bool peptideLevelCalculati // e.g. if the PSM matched to 1 target and 2 decoys, it counts as 2/3 decoy double decoyHits = 0; double totalHits = 0; - var hits = psm.BestMatchingBioPolymersWithSetMods.GroupBy(p => p.Peptide.FullSequence); + var hits = psm.BestMatchingBioPolymersWithSetMods.GroupBy(p => p.FullSequence); foreach (var hit in hits) { - if (hit.First().Peptide.Parent.IsDecoy) + if (hit.First().IsDecoy) { decoyHits++; } diff --git a/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs b/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs index efbbe6b6bd..d6755809f6 100644 --- a/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs +++ b/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs @@ -18,6 +18,7 @@ using Omics; using Easy.Common.Extensions; using System.Threading; +using EngineLayer.SpectrumMatch; namespace EngineLayer { @@ -274,12 +275,12 @@ public IEnumerable CreatePsmData(string searchType, if (csm.IsDecoy || csm.BetaPeptide.IsDecoy) { label = false; - newPsmData = CreateOnePsmDataEntry(searchType, csm, csm.BestMatchingBioPolymersWithSetMods.First().Peptide, 0, label); + newPsmData = CreateOnePsmDataEntry(searchType, csm, csm.BestMatchingBioPolymersWithSetMods.First(), label); } else if (!csm.IsDecoy && !csm.BetaPeptide.IsDecoy && csm.GetFdrInfo(UsePeptideLevelQValueForTraining).QValue <= QValueCutoff) { label = true; - newPsmData = CreateOnePsmDataEntry(searchType, csm, csm.BestMatchingBioPolymersWithSetMods.First().Peptide, 0, label); + newPsmData = CreateOnePsmDataEntry(searchType, csm, csm.BestMatchingBioPolymersWithSetMods.First(), label); } else { @@ -290,22 +291,20 @@ public IEnumerable CreatePsmData(string searchType, else { double bmp = 0; - foreach (var (notch, peptideWithSetMods) in psm.BestMatchingBioPolymersWithSetMods) + foreach (TentativeSpectralMatch bestMatch in psm.BestMatchingBioPolymersWithSetMods) { bool label; double bmpc = psm.BestMatchingBioPolymersWithSetMods.Count(); - if (peptideWithSetMods.Parent.IsDecoy) + if (bestMatch.WithSetMods.Parent.IsDecoy) { label = false; - newPsmData = CreateOnePsmDataEntry(searchType, psm, - peptideWithSetMods, notch, label); + newPsmData = CreateOnePsmDataEntry(searchType, psm, bestMatch, label); } - else if (!peptideWithSetMods.Parent.IsDecoy + else if (!bestMatch.WithSetMods.Parent.IsDecoy && psm.GetFdrInfo(UsePeptideLevelQValueForTraining).QValue <= QValueCutoff) { label = true; - newPsmData = CreateOnePsmDataEntry(searchType, psm, - peptideWithSetMods, notch, label); + newPsmData = CreateOnePsmDataEntry(searchType, psm, bestMatch, label); } else { @@ -424,6 +423,8 @@ public int Compute_PSM_PEP(List peptideGroups, int ambigousPeptidesRemovedinThread = 0; + List indiciesOfPeptidesToRemove = new List(); + List pepValuePredictions = new List(); for (int i = range.Item1; i < range.Item2; i++) { foreach (SpectralMatch psm in peptideGroups[peptideGroupIndices[i]]) @@ -431,28 +432,21 @@ public int Compute_PSM_PEP(List peptideGroups, // I'm not sure what's going one here vis-a-vis disambiguations, but I'm not going to touch it for now if (psm != null) { - List indiciesOfPeptidesToRemove = new List(); - List pepValuePredictions = new List(); + indiciesOfPeptidesToRemove.Clear(); + pepValuePredictions.Clear(); //Here we compute the pepvalue predection for each ambiguous peptide in a PSM. Ambiguous peptides with lower pepvalue predictions are removed from the PSM. - - List allBmpNotches = new List(); - List allBmpPeptides = new List(); - - foreach (var (Notch, Peptide) in psm.BestMatchingBioPolymersWithSetMods) + var bestMatchingBioPolymersWithSetMods = psm.BestMatchingBioPolymersWithSetMods.ToList(); + foreach (TentativeSpectralMatch bestMatch in bestMatchingBioPolymersWithSetMods) { - allBmpNotches.Add(Notch); - allBmpPeptides.Add(Peptide); - PsmData pd = CreateOnePsmDataEntry(searchType, psm, Peptide, Notch, !Peptide.Parent.IsDecoy); + PsmData pd = CreateOnePsmDataEntry(searchType, psm, bestMatch, !bestMatch.IsDecoy); var pepValuePrediction = threadPredictionEngine.Predict(pd); pepValuePredictions.Add(pepValuePrediction.Probability); //A score is available using the variable pepvaluePrediction.Score } GetIndiciesOfPeptidesToRemove(indiciesOfPeptidesToRemove, pepValuePredictions); - int peptidesRemoved = 0; - RemoveBestMatchingPeptidesWithLowPEP(psm, indiciesOfPeptidesToRemove, allBmpNotches, allBmpPeptides, pepValuePredictions, ref peptidesRemoved); - ambigousPeptidesRemovedinThread += peptidesRemoved; + RemoveBestMatchingPeptidesWithLowPEP(psm, indiciesOfPeptidesToRemove, bestMatchingBioPolymersWithSetMods, ref ambigousPeptidesRemovedinThread); psm.PsmFdrInfo.PEP = 1 - pepValuePredictions.Max(); psm.PeptideFdrInfo.PEP = 1 - pepValuePredictions.Max(); @@ -466,9 +460,9 @@ public int Compute_PSM_PEP(List peptideGroups, return ambiguousPeptidesResolved; } - public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, IBioPolymerWithSetMods selectedPeptide, int notchToUse, bool label) + public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, TentativeSpectralMatch tentativeSpectralMatch, bool label) { - double normalizationFactor = selectedPeptide.BaseSequence.Length; + double normalizationFactor = tentativeSpectralMatch.WithSetMods.BaseSequence.Length; float totalMatchingFragmentCount = 0; float internalMatchingFragmentCount = 0; float intensity = 0; @@ -509,23 +503,23 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, IBioP normalizationFactor = 1.0; } // count only terminal fragment ions - totalMatchingFragmentCount = (float)(Math.Round(psm.BioPolymersWithSetModsToMatchingFragments[selectedPeptide].Count(p => p.NeutralTheoreticalProduct.SecondaryProductType == null) / normalizationFactor * multiplier, 0)); - internalMatchingFragmentCount = (float)(Math.Round(psm.BioPolymersWithSetModsToMatchingFragments[selectedPeptide].Count(p => p.NeutralTheoreticalProduct.SecondaryProductType != null) / normalizationFactor * multiplier, 0)); + totalMatchingFragmentCount = (float)(Math.Round(tentativeSpectralMatch.MatchedIons.Count(p => p.NeutralTheoreticalProduct.SecondaryProductType == null) / normalizationFactor * multiplier, 0)); + internalMatchingFragmentCount = (float)(Math.Round(tentativeSpectralMatch.MatchedIons.Count(p => p.NeutralTheoreticalProduct.SecondaryProductType != null) / normalizationFactor * multiplier, 0)); intensity = (float)Math.Min(50, Math.Round((psm.Score - (int)psm.Score) / normalizationFactor * Math.Pow(multiplier, 2), 0)); chargeDifference = -Math.Abs(ChargeStateMode - psm.ScanPrecursorCharge); deltaScore = (float)Math.Round(psm.DeltaScore / normalizationFactor * multiplier, 0); - notch = notchToUse; - modCount = Math.Min((float)selectedPeptide.AllModsOneIsNterminus.Keys.Count(), 10); - if (psm.BioPolymersWithSetModsToMatchingFragments[selectedPeptide]?.Count() > 0) + notch = tentativeSpectralMatch.Notch; + modCount = Math.Min((float)tentativeSpectralMatch.WithSetMods.AllModsOneIsNterminus.Keys.Count(), 10); + if (tentativeSpectralMatch.MatchedIons?.Count() > 0) { - absoluteFragmentMassError = (float)Math.Min(100.0, Math.Round(10.0 * Math.Abs(GetAverageFragmentMassError(psm.BioPolymersWithSetModsToMatchingFragments[selectedPeptide]) - FileSpecificMedianFragmentMassErrors[Path.GetFileName(psm.FullFilePath)]))); + absoluteFragmentMassError = (float)Math.Min(100.0, Math.Round(10.0 * Math.Abs(GetAverageFragmentMassError(tentativeSpectralMatch.MatchedIons) - FileSpecificMedianFragmentMassErrors[Path.GetFileName(psm.FullFilePath)]))); } - ambiguity = Math.Min((float)(psm.BioPolymersWithSetModsToMatchingFragments.Keys.Count - 1), 10); + ambiguity = Math.Min((float)(psm.BestMatchingBioPolymersWithSetMods.Count() - 1), 10); //ambiguity = 10; // I'm pretty sure that you shouldn't train on ambiguity and its skewing the results - longestSeq = (float)Math.Round(SpectralMatch.GetLongestIonSeriesBidirectional(psm.BioPolymersWithSetModsToMatchingFragments, selectedPeptide) / normalizationFactor * multiplier, 0); - complementaryIonCount = (float)Math.Round(SpectralMatch.GetCountComplementaryIons(psm.BioPolymersWithSetModsToMatchingFragments, selectedPeptide) / normalizationFactor * multiplier, 0); - isVariantPeptide = PeptideIsVariant(selectedPeptide); + longestSeq = (float)Math.Round(SpectralMatch.GetLongestIonSeriesBidirectional(tentativeSpectralMatch.MatchedIons, tentativeSpectralMatch.WithSetMods) / normalizationFactor * multiplier, 0); + complementaryIonCount = (float)Math.Round(SpectralMatch.GetCountComplementaryIons(tentativeSpectralMatch.MatchedIons, tentativeSpectralMatch.WithSetMods) / normalizationFactor * multiplier, 0); + isVariantPeptide = PeptideIsVariant(tentativeSpectralMatch.WithSetMods); spectralAngle = (float)psm.SpectralAngle; if (chimeraCountDictionary.TryGetValue(psm.ChimeraIdString, out int val)) chimeraCount = val; @@ -540,23 +534,23 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, IBioP if (psm.DigestionParams.Protease.Name != "top-down") { - missedCleavages = selectedPeptide.MissedCleavages; + missedCleavages = tentativeSpectralMatch.WithSetMods.MissedCleavages; bool fileIsCzeSeparationType = FileSpecificParametersDictionary.ContainsKey(Path.GetFileName(psm.FullFilePath)) && FileSpecificParametersDictionary[Path.GetFileName(psm.FullFilePath)].SeparationType == "CZE"; if (!fileIsCzeSeparationType) { - if (selectedPeptide.BaseSequence.Equals(selectedPeptide.FullSequence)) + if (tentativeSpectralMatch.WithSetMods.BaseSequence.Equals(tentativeSpectralMatch.WithSetMods.FullSequence)) { - hydrophobicityZscore = (float)Math.Round(GetSSRCalcHydrophobicityZScore(psm, selectedPeptide, FileSpecificTimeDependantHydrophobicityAverageAndDeviation_unmodified) * 10.0, 0); + hydrophobicityZscore = (float)Math.Round(GetSSRCalcHydrophobicityZScore(psm, tentativeSpectralMatch.WithSetMods, FileSpecificTimeDependantHydrophobicityAverageAndDeviation_unmodified) * 10.0, 0); } else { - hydrophobicityZscore = (float)Math.Round(GetSSRCalcHydrophobicityZScore(psm, selectedPeptide, FileSpecificTimeDependantHydrophobicityAverageAndDeviation_modified) * 10.0, 0); + hydrophobicityZscore = (float)Math.Round(GetSSRCalcHydrophobicityZScore(psm, tentativeSpectralMatch.WithSetMods, FileSpecificTimeDependantHydrophobicityAverageAndDeviation_modified) * 10.0, 0); } } else { - hydrophobicityZscore = (float)Math.Round(GetMobilityZScore(psm, selectedPeptide) * 10.0, 0); + hydrophobicityZscore = (float)Math.Round(GetMobilityZScore(psm, tentativeSpectralMatch.WithSetMods) * 10.0, 0); } } //this is not for actual crosslinks but for the byproducts of crosslink loop links, deadends, etc. @@ -570,11 +564,11 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, IBioP else { CrosslinkSpectralMatch csm = (CrosslinkSpectralMatch)psm; - PeptideWithSetModifications selectedAlphaPeptide = csm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide as PeptideWithSetModifications).First(); - PeptideWithSetModifications selectedBetaPeptide = csm.BetaPeptide?.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide as PeptideWithSetModifications).First(); + var selectedAlphaPeptide = csm.BestMatchingBioPolymersWithSetMods.First(); + var selectedBetaPeptide = csm.BetaPeptide?.BestMatchingBioPolymersWithSetMods.First(); - float alphaNormalizationFactor = selectedAlphaPeptide.BaseSequence.Length; - float betaNormalizationFactor = selectedBetaPeptide == null ? (float)0 : selectedBetaPeptide.BaseSequence.Length; + float alphaNormalizationFactor = selectedAlphaPeptide.WithSetMods.BaseSequence.Length; + float betaNormalizationFactor = selectedBetaPeptide == null ? (float)0 : selectedBetaPeptide.WithSetMods.BaseSequence.Length; float totalNormalizationFactor = alphaNormalizationFactor + betaNormalizationFactor; totalMatchingFragmentCount = (float)Math.Round(csm.XLTotalScore / totalNormalizationFactor * 10, 0); @@ -582,17 +576,17 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, IBioP //Compute fragment mass error int alphaCount = 0; float alphaError = 0; - if (csm.BioPolymersWithSetModsToMatchingFragments[selectedAlphaPeptide]?.Count > 0) + if (selectedAlphaPeptide.MatchedIons?.Count > 0) { - alphaCount = csm.BioPolymersWithSetModsToMatchingFragments[selectedAlphaPeptide].Count; - alphaError = Math.Abs(GetAverageFragmentMassError(csm.BioPolymersWithSetModsToMatchingFragments[selectedAlphaPeptide])); + alphaCount = selectedAlphaPeptide.MatchedIons.Count; + alphaError = Math.Abs(GetAverageFragmentMassError(selectedAlphaPeptide.MatchedIons)); } int betaCount = 0; float betaError = 0; - if (selectedBetaPeptide != null && csm.BetaPeptide.BioPolymersWithSetModsToMatchingFragments[selectedBetaPeptide]?.Count > 0) + if (selectedBetaPeptide != null && selectedBetaPeptide.MatchedIons?.Count > 0) { - betaCount = csm.BetaPeptide.BioPolymersWithSetModsToMatchingFragments[selectedBetaPeptide].Count; - betaError = Math.Abs(GetAverageFragmentMassError(csm.BetaPeptide.BioPolymersWithSetModsToMatchingFragments[selectedBetaPeptide])); + betaCount = selectedBetaPeptide.MatchedIons.Count; + betaError = Math.Abs(GetAverageFragmentMassError(selectedBetaPeptide.MatchedIons)); } float averageError = 0; @@ -608,8 +602,8 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, IBioP chargeDifference = -Math.Abs(ChargeStateMode - psm.ScanPrecursorCharge); alphaIntensity = (float)Math.Min(100, Math.Round((csm.Score - (int)csm.Score) / alphaNormalizationFactor * 100.0, 0)); betaIntensity = csm.BetaPeptide == null ? (float)0 : (float)Math.Min(100.0, Math.Round((csm.BetaPeptide.Score - (int)csm.BetaPeptide.Score) / betaNormalizationFactor * 100.0, 0)); - longestFragmentIonSeries_Alpha = (float)Math.Round(SpectralMatch.GetLongestIonSeriesBidirectional(csm.BioPolymersWithSetModsToMatchingFragments, selectedAlphaPeptide) / alphaNormalizationFactor * 10.0, 0); - longestFragmentIonSeries_Beta = selectedBetaPeptide == null ? (float)0 : SpectralMatch.GetLongestIonSeriesBidirectional(csm.BetaPeptide.BioPolymersWithSetModsToMatchingFragments, selectedBetaPeptide) / betaNormalizationFactor; + longestFragmentIonSeries_Alpha = (float)Math.Round(SpectralMatch.GetLongestIonSeriesBidirectional(selectedAlphaPeptide.MatchedIons, selectedAlphaPeptide.WithSetMods) / alphaNormalizationFactor * 10.0, 0); + longestFragmentIonSeries_Beta = selectedBetaPeptide == null ? (float)0 : SpectralMatch.GetLongestIonSeriesBidirectional(selectedBetaPeptide.MatchedIons, selectedBetaPeptide.WithSetMods) / betaNormalizationFactor; longestFragmentIonSeries_Beta = (float)Math.Round(longestFragmentIonSeries_Beta * 10.0, 0); isInter = Convert.ToSingle(csm.CrossType == PsmCrossType.Inter); isIntra = Convert.ToSingle(csm.CrossType == PsmCrossType.Intra); @@ -654,13 +648,15 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, IBioP return psm.PsmData_forPEPandPercolator; } - public static void RemoveBestMatchingPeptidesWithLowPEP(SpectralMatch psm, List indiciesOfPeptidesToRemove, List notches, List pwsmList, List pepValuePredictions, ref int ambiguousPeptidesRemovedCount) + public static void RemoveBestMatchingPeptidesWithLowPEP(SpectralMatch psm, List indiciesOfPeptidesToRemove, List allPeptides, ref int ambiguousPeptidesRemovedCount) { - foreach (int i in indiciesOfPeptidesToRemove) + int peptidesRemoved = 0; + foreach (var toRemove in indiciesOfPeptidesToRemove) { - psm.RemoveThisAmbiguousPeptide(notches[i], pwsmList[i]); - ambiguousPeptidesRemovedCount++; + psm.RemoveThisAmbiguousPeptide(allPeptides[toRemove - peptidesRemoved]); + peptidesRemoved++; } + ambiguousPeptidesRemovedCount += peptidesRemoved; } /// @@ -713,21 +709,21 @@ public Dictionary>> ComputeHydroph foreach (SpectralMatch psm in psms.Where(f => (f.FullFilePath == null || Path.GetFileName(f.FullFilePath) == filename) && f.FdrInfo.QValue <= 0.01 && !f.IsDecoy)) { List fullSequences = new List(); - foreach ((int notch, IBioPolymerWithSetMods pwsm) in psm.BestMatchingBioPolymersWithSetMods) + foreach (TentativeSpectralMatch bestMatch in psm.BestMatchingBioPolymersWithSetMods) { - if (fullSequences.Contains(pwsm.FullSequence)) + if (fullSequences.Contains(bestMatch.WithSetMods.FullSequence)) { continue; } - fullSequences.Add(pwsm.FullSequence); + fullSequences.Add(bestMatch.WithSetMods.FullSequence); - double predictedHydrophobicity = pwsm is PeptideWithSetModifications pep ? calc.ScoreSequence(pep) : 0; + double predictedHydrophobicity = bestMatch.WithSetMods is PeptideWithSetModifications pep ? calc.ScoreSequence(pep) : 0; //here i'm grouping this in 2 minute increments becuase there are cases where you get too few data points to get a good standard deviation an average. This is for stability. int possibleKey = (int)(2 * Math.Round(psm.ScanRetentionTime / 2d, 0)); //First block of if statement is for modified peptides. - if (pwsm.AllModsOneIsNterminus.Any() && computeHydrophobicitiesforModifiedPeptides) + if (bestMatch.WithSetMods.AllModsOneIsNterminus.Any() && computeHydrophobicitiesforModifiedPeptides) { if (hydrophobicities.ContainsKey(possibleKey)) { @@ -739,7 +735,7 @@ public Dictionary>> ComputeHydroph } } //this second block of if statment is for unmodified peptides. - else if (!pwsm.AllModsOneIsNterminus.Any() && !computeHydrophobicitiesforModifiedPeptides) + else if (!bestMatch.WithSetMods.AllModsOneIsNterminus.Any() && !computeHydrophobicitiesforModifiedPeptides) { if (hydrophobicities.ContainsKey(possibleKey)) { @@ -815,15 +811,15 @@ public Dictionary>> ComputeMobilit foreach (SpectralMatch psm in psms.Where(f => (f.FullFilePath == null || Path.GetFileName(f.FullFilePath) == filename) && f.FdrInfo.QValue <= 0.01 && !f.IsDecoy)) { List fullSequences = new List(); - foreach ((int notch, IBioPolymerWithSetMods pwsm) in psm.BestMatchingBioPolymersWithSetMods) + foreach (TentativeSpectralMatch bestMatch in psm.BestMatchingBioPolymersWithSetMods) { - if (fullSequences.Contains(pwsm.FullSequence)) + if (fullSequences.Contains(bestMatch.WithSetMods.FullSequence)) { continue; } - fullSequences.Add(pwsm.FullSequence); + fullSequences.Add(bestMatch.WithSetMods.FullSequence); - double predictedMobility = pwsm is PeptideWithSetModifications pep ? 100.0 * GetCifuentesMobility(pep) : 0; + double predictedMobility = bestMatch.WithSetMods is PeptideWithSetModifications pep ? 100.0 * GetCifuentesMobility(pep) : 0; //here i'm grouping this in 2 minute increments becuase there are cases where you get too few data points to get a good standard deviation an average. This is for stability. int possibleKey = (int)(2 * Math.Round(psm.ScanRetentionTime / 2d, 0)); @@ -1021,11 +1017,11 @@ public static float GetMedianAverageMassError(IEnumerable psms) foreach (SpectralMatch psm in psms) { { - foreach (KeyValuePair> peptide_MFI in psm.BioPolymersWithSetModsToMatchingFragments) + foreach (var bestMatch in psm.BestMatchingBioPolymersWithSetMods) { - if (peptide_MFI.Value != null && peptide_MFI.Value.Count > 0) + if (bestMatch.MatchedIons is { Count: > 0 }) { - averageMassErrors.Add(GetAverageFragmentMassError(peptide_MFI.Value)); + averageMassErrors.Add(GetAverageFragmentMassError(bestMatch.MatchedIons)); } } } diff --git a/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSearchEngine.cs b/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSearchEngine.cs index af9ba0e58e..6e9996df5b 100644 --- a/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSearchEngine.cs +++ b/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSearchEngine.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using EngineLayer; using MassSpectrometry; +using EngineLayer.SpectrumMatch; namespace EngineLayer.GlycoSearch { @@ -260,10 +261,9 @@ private void Add2GlobalGsms(ref List gsms, int scanIndex) if (preString == currentString) //If peptides have the same sequence and their score is almost the same { - foreach ((int, PeptideWithSetModifications Peptide) bestMatchPeptide in gsm.BestMatchingBioPolymersWithSetMods) // We should add tje new ProteinMatch to the gsm. + foreach (TentativeSpectralMatch bestMatchPeptide in gsm.BestMatchingBioPolymersWithSetMods) // We should add tje new ProteinMatch to the gsm. { // Because the indentical sequence may from the different protein. - GlobalGsms[scanIndex].Last().AddProteinMatch(bestMatchPeptide, gsm.BioPolymersWithSetModsToMatchingFragments[bestMatchPeptide.Peptide]); - + GlobalGsms[scanIndex].Last().AddProteinMatch(bestMatchPeptide); } } else diff --git a/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSpectralMatch.cs b/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSpectralMatch.cs index ec29d613cc..1f662434a9 100644 --- a/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSpectralMatch.cs @@ -203,12 +203,12 @@ public string SingleToString() sb.Append(ScanPrecursorCharge + "\t"); sb.Append(ScanPrecursorMass + "\t"); - var proteinAccessionString = Accession ?? PsmTsvWriter.Resolve(BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide.Parent.Accession), FullSequence).ResolvedString; + var proteinAccessionString = Accession ?? PsmTsvWriter.Resolve(BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods.Parent.Accession), FullSequence).ResolvedString; sb.Append(proteinAccessionString + "\t"); sb.Append(Organism + "\t"); - sb.Append(PsmTsvWriter.Resolve(BestMatchingBioPolymersWithSetMods.Select(b => b.Peptide.Parent.FullName), FullSequence).ResolvedString + "\t"); //protein name - int _FirstOneBasedStartResidueInProtein = OneBasedStartResidue.HasValue ? OneBasedStartResidue.Value : BestMatchingBioPolymersWithSetMods.First().Peptide.OneBasedStartResidue; - int _FirstOneBasedEndResidueInProtein = OneBasedEndResidue.HasValue ? OneBasedEndResidue.Value : BestMatchingBioPolymersWithSetMods.First().Peptide.OneBasedEndResidue; ; + sb.Append(PsmTsvWriter.Resolve(BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.Parent.FullName), FullSequence).ResolvedString + "\t"); //protein name + int _FirstOneBasedStartResidueInProtein = OneBasedStartResidue.HasValue ? OneBasedStartResidue.Value : BestMatchingBioPolymersWithSetMods.First().WithSetMods.OneBasedStartResidue; + int _FirstOneBasedEndResidueInProtein = OneBasedEndResidue.HasValue ? OneBasedEndResidue.Value : BestMatchingBioPolymersWithSetMods.First().WithSetMods.OneBasedEndResidue; ; if (OneBasedStartResidue.HasValue) { @@ -220,9 +220,9 @@ public string SingleToString() } sb.Append(BaseSequence + "\t"); - sb.Append(BestMatchingBioPolymersWithSetMods.First().Peptide.PreviousResidue + "," + BestMatchingBioPolymersWithSetMods.First().Peptide.NextResidue + "\t"); + sb.Append(BestMatchingBioPolymersWithSetMods.First().WithSetMods.PreviousResidue + "," + BestMatchingBioPolymersWithSetMods.First().WithSetMods.NextResidue + "\t"); sb.Append(FullSequence + "\t"); - sb.Append(BestMatchingBioPolymersWithSetMods.First().Peptide.AllModsOneIsNterminus.Count + "\t"); + sb.Append(BestMatchingBioPolymersWithSetMods.First().WithSetMods.AllModsOneIsNterminus.Count + "\t"); sb.Append((BioPolymerWithSetModsMonoisotopicMass.HasValue ? BioPolymerWithSetModsMonoisotopicMass.Value.ToString() : "---")); sb.Append("\t"); sb.Append(Score + "\t"); diff --git a/MetaMorpheus/EngineLayer/Gptmd/GptmdEngine.cs b/MetaMorpheus/EngineLayer/Gptmd/GptmdEngine.cs index f5482ad4d4..8d5313e939 100644 --- a/MetaMorpheus/EngineLayer/Gptmd/GptmdEngine.cs +++ b/MetaMorpheus/EngineLayer/Gptmd/GptmdEngine.cs @@ -78,7 +78,7 @@ protected override MetaMorpheusEngineResults RunSpecific() { for (int i = range.Item1; i < range.Item2; i++) { - foreach (var pepWithSetMods in psms[i].BestMatchingBioPolymersWithSetMods.Select(v => v.Peptide as PeptideWithSetModifications)) + foreach (var pepWithSetMods in psms[i].BestMatchingBioPolymersWithSetMods.Select(v => v.WithSetMods as PeptideWithSetModifications)) { var isVariantProtein = pepWithSetMods.Parent != pepWithSetMods.Protein.NonVariantProtein; var possibleModifications = GetPossibleMods(psms[i].ScanPrecursorMass, GptmdModifications, Combos, FilePathToPrecursorMassTolerance[psms[i].FullFilePath], pepWithSetMods); diff --git a/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs b/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs index 97ac5f5250..9c74541919 100644 --- a/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs +++ b/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs @@ -51,7 +51,7 @@ protected override MetaMorpheusEngineResults RunSpecific() MsDataScan scan = MyMsDataFile.GetOneBasedScan(psm.ScanNumber); Ms2ScanWithSpecificMass scanWithSpecificMass = new Ms2ScanWithSpecificMass(scan, psm.ScanPrecursorMonoisotopicPeakMz, psm.ScanPrecursorCharge, psm.FullFilePath, CommonParameters); - IBioPolymerWithSetMods peptide = psm.BestMatchingBioPolymersWithSetMods.First().Peptide; + IBioPolymerWithSetMods peptide = psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods; double massDifference = psm.ScanPrecursorMass - peptide.MonoisotopicMass; // this section will iterate through all residues of the peptide and try to localize the mass-diff at each residue and report a score for each residue diff --git a/MetaMorpheus/EngineLayer/ModificationAnalysis/ModificationAnalysisEngine.cs b/MetaMorpheus/EngineLayer/ModificationAnalysis/ModificationAnalysisEngine.cs index 209196e10c..d8ff78519a 100644 --- a/MetaMorpheus/EngineLayer/ModificationAnalysis/ModificationAnalysisEngine.cs +++ b/MetaMorpheus/EngineLayer/ModificationAnalysis/ModificationAnalysisEngine.cs @@ -59,7 +59,7 @@ protected override MetaMorpheusEngineResults RunSpecific() HashSet<(string, string, int)> modsOnProteins = new HashSet<(string, string, int)>(); foreach (var psm in forObserved) { - var singlePeptide = psm.BestMatchingBioPolymersWithSetMods.First().Peptide; + var singlePeptide = psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods; foreach (var modInProtein in singlePeptide.Parent.OneBasedPossibleLocalizedModifications.Where(b => b.Key >= singlePeptide.OneBasedStartResidue && b.Key <= singlePeptide.OneBasedEndResidue)) foreach (var huh in modInProtein.Value) @@ -70,7 +70,7 @@ protected override MetaMorpheusEngineResults RunSpecific() HashSet<(string, string, int)> modsSeenAndLocalized = new HashSet<(string, string, int)>(); foreach (var psm in forUnambiguouslyLocalized) { - var singlePeptide = psm.BestMatchingBioPolymersWithSetMods.First().Peptide; + var singlePeptide = psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods; foreach (var nice in singlePeptide.AllModsOneIsNterminus) { int locInProtein; diff --git a/MetaMorpheus/EngineLayer/OligoSpectralMatch.cs b/MetaMorpheus/EngineLayer/OligoSpectralMatch.cs index d321302888..21f959c23f 100644 --- a/MetaMorpheus/EngineLayer/OligoSpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/OligoSpectralMatch.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using EngineLayer.SpectrumMatch; using Omics; using Omics.Fragmentation; @@ -19,7 +20,7 @@ public OligoSpectralMatch(IBioPolymerWithSetMods peptide, int notch, double scor } - protected OligoSpectralMatch(SpectralMatch psm, List<(int Notch, IBioPolymerWithSetMods Peptide)> bestMatchingPeptides) + protected OligoSpectralMatch(SpectralMatch psm, List bestMatchingPeptides) : base(psm, bestMatchingPeptides) { } diff --git a/MetaMorpheus/EngineLayer/PeptideSpectralMatch.cs b/MetaMorpheus/EngineLayer/PeptideSpectralMatch.cs index 1e23205e01..7dcf9a95c4 100644 --- a/MetaMorpheus/EngineLayer/PeptideSpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/PeptideSpectralMatch.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using EngineLayer.SpectrumMatch; using Omics; using Omics.Fragmentation; using Omics.Modifications; @@ -25,15 +26,15 @@ public PeptideSpectralMatch(IBioPolymerWithSetMods peptide, int notch, double sc public void ResolveHeavySilacLabel(List labels, IReadOnlyDictionary modsToWritePruned) { //FullSequence - FullSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.FullSequence)).ResolvedString; //string, not value + FullSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.FullSequence)).ResolvedString; //string, not value FullSequence = SilacConversions.GetAmbiguousLightSequence(FullSequence, labels, false); //BaseSequence - BaseSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.BaseSequence)).ResolvedString; //string, not value + BaseSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.BaseSequence)).ResolvedString; //string, not value BaseSequence = SilacConversions.GetAmbiguousLightSequence(BaseSequence, labels, true); //EssentialSequence - EssentialSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.EssentialSequence(modsToWritePruned))).ResolvedString; //string, not value + EssentialSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.EssentialSequence(modsToWritePruned))).ResolvedString; //string, not value EssentialSequence = SilacConversions.GetAmbiguousLightSequence(EssentialSequence, labels, false); } @@ -41,9 +42,9 @@ public void ResolveHeavySilacLabel(List labels, IReadOnlyDictionary< /// This method is used by SILAC quantification to add heavy/light psms /// Don't have access to the scans at that point, so a new contructor is needed /// - public PeptideSpectralMatch Clone(List<(int Notch, IBioPolymerWithSetMods Peptide)> bestMatchingPeptides) => new PeptideSpectralMatch(this, bestMatchingPeptides); + public PeptideSpectralMatch Clone(List bestMatchingPeptides) => new PeptideSpectralMatch(this, bestMatchingPeptides); - protected PeptideSpectralMatch(SpectralMatch psm, List<(int Notch, IBioPolymerWithSetMods Peptide)> bestMatchingPeptides) + protected PeptideSpectralMatch(SpectralMatch psm, List bestMatchingPeptides) : base(psm, bestMatchingPeptides) { } diff --git a/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinGroup.cs b/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinGroup.cs index e882391b02..23bf00e236 100644 --- a/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinGroup.cs +++ b/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinGroup.cs @@ -404,7 +404,7 @@ public void CalculateSequenceCoverage() { psm.GetAminoAcidCoverage(); - foreach (var peptide in psm.BestMatchingBioPolymersWithSetMods.Select(psm => psm.Peptide as PeptideWithSetModifications).DistinctBy(pep => pep.FullSequence)) + foreach (var peptide in psm.BestMatchingBioPolymersWithSetMods.Select(psm => psm.WithSetMods as PeptideWithSetModifications).DistinctBy(pep => pep.FullSequence)) { // might be unambiguous but also shared; make sure this protein group contains this peptide+protein combo if (Proteins.Contains(peptide.Protein)) @@ -436,7 +436,7 @@ public void CalculateSequenceCoverage() psm.GetAminoAcidCoverage(); if (psm.FragmentCoveragePositionInPeptide == null) continue; //loop through each peptide within the psm - IEnumerable pwsms = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide as PeptideWithSetModifications) + IEnumerable pwsms = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods as PeptideWithSetModifications) .Where(p => p.Protein.Accession == protein.Accession); foreach (PeptideWithSetModifications pwsm in pwsms) { @@ -648,7 +648,7 @@ public ProteinGroup ConstructSubsetProteinGroup(string fullFilePath, List p.FullFilePath.Equals(fullFilePath))); var allPeptidesForThisFile = new HashSet( - allPsmsForThisFile.SelectMany(p => p.BestMatchingBioPolymersWithSetMods.Select(v => v.Peptide as PeptideWithSetModifications))); + allPsmsForThisFile.SelectMany(p => p.BestMatchingBioPolymersWithSetMods.Select(v => v.WithSetMods as PeptideWithSetModifications))); var allUniquePeptidesForThisFile = new HashSet(UniquePeptides.Intersect(allPeptidesForThisFile)); diff --git a/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinParsimonyEngine.cs b/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinParsimonyEngine.cs index 081aab8009..0eb450051f 100644 --- a/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinParsimonyEngine.cs +++ b/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinParsimonyEngine.cs @@ -52,7 +52,7 @@ public ProteinParsimonyEngine(List allPsms, bool modPeptidesAreDi _fdrFilteredPeptides = new HashSet(); foreach (var psm in _fdrFilteredPsms) { - foreach (var peptide in psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide)) + foreach (var peptide in psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods)) { _fdrFilteredPeptides.Add(peptide); } @@ -127,12 +127,12 @@ private List RunProteinParsimonyEngine() var peptidesWithNotchInfo = baseSequence.Value.SelectMany(p => p.BestMatchingBioPolymersWithSetMods).Distinct().ToList(); // if the base seq has >1 PeptideWithSetMods object and has >0 mods, it might need to be matched to new proteins - if (peptidesWithNotchInfo.Count > 1 && peptidesWithNotchInfo.Any(p => p.Peptide.NumMods > 0)) + if (peptidesWithNotchInfo.Count > 1 && peptidesWithNotchInfo.Any(p => p.WithSetMods.NumMods > 0)) { bool needToAddPeptideToProteinAssociations = false; // numProteinsForThisBaseSequence is the total number of proteins that this base sequence is a digestion product of - int numProteinsForThisBaseSequence = peptidesWithNotchInfo.Select(p => p.Peptide.Parent).Distinct().Count(); + int numProteinsForThisBaseSequence = peptidesWithNotchInfo.Select(p => p.WithSetMods.Parent).Distinct().Count(); if (numProteinsForThisBaseSequence == 1) { @@ -142,7 +142,7 @@ private List RunProteinParsimonyEngine() foreach (var psm in baseSequence.Value) { // numProteinsForThisPsm is the number of proteins that this PSM's peptides are associated with - int numProteinsForThisPsm = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide.Parent).Distinct().Count(); + int numProteinsForThisPsm = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods.Parent).Distinct().Count(); if (numProteinsForThisPsm != numProteinsForThisBaseSequence) { @@ -166,18 +166,18 @@ private List RunProteinParsimonyEngine() { foreach (var peptideWithNotch in psm.BestMatchingBioPolymersWithSetMods) { - PeptideWithSetModifications peptide = peptideWithNotch.Peptide as PeptideWithSetModifications; + PeptideWithSetModifications peptide = peptideWithNotch.WithSetMods as PeptideWithSetModifications; Protein protein = peptide.Protein; if (!proteinToPeptideInfo.ContainsKey(protein)) { proteinToPeptideInfo.Add(protein, - (peptideWithNotch.Peptide.DigestionParams, - peptideWithNotch.Peptide.OneBasedStartResidue, - peptideWithNotch.Peptide.OneBasedEndResidue, - peptideWithNotch.Peptide.MissedCleavages, + (peptideWithNotch.WithSetMods.DigestionParams, + peptideWithNotch.WithSetMods.OneBasedStartResidue, + peptideWithNotch.WithSetMods.OneBasedEndResidue, + peptideWithNotch.WithSetMods.MissedCleavages, peptideWithNotch.Notch, - peptideWithNotch.Peptide.CleavageSpecificityForFdrCategory)); + peptideWithNotch.WithSetMods.CleavageSpecificityForFdrCategory)); } } } @@ -185,9 +185,10 @@ private List RunProteinParsimonyEngine() // create any new associations that need to be made foreach (PeptideSpectralMatch psm in baseSequence.Value) { - IBioPolymerWithSetMods originalPeptide = psm.BestMatchingBioPolymersWithSetMods.First().Peptide; - List mfi = psm.BioPolymersWithSetModsToMatchingFragments[originalPeptide]; - HashSet psmProteins = new HashSet(psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide.Parent as Protein)); + var tentativeMatch = psm.BestMatchingBioPolymersWithSetMods.First(); + IBioPolymerWithSetMods originalPeptide = tentativeMatch.WithSetMods; + List mfi = tentativeMatch.MatchedIons; + HashSet psmProteins = new HashSet(psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods.Parent as Protein)); foreach (var proteinWithDigestInfo in proteinToPeptideInfo) { @@ -209,7 +210,7 @@ private List RunProteinParsimonyEngine() _fdrFilteredPeptides.Add(pep); } - psm.AddProteinMatch((proteinWithDigestInfo.Value.Notch, pep), mfi); + psm.AddProteinMatch(new(proteinWithDigestInfo.Value.Notch, pep, mfi)); } } } @@ -432,7 +433,7 @@ private List RunProteinParsimonyEngine() // if this PSM has a protein in the parsimonious list, it removes the proteins NOT in the parsimonious list // otherwise, no proteins are removed (i.e., for PSMs that cannot be explained by a parsimonious protein, // no protein associations are removed) - if (psm.BestMatchingBioPolymersWithSetMods.Any(p => parsimoniousProteinList.Contains(p.Peptide.Parent as Protein))) + if (psm.BestMatchingBioPolymersWithSetMods.Any(p => parsimoniousProteinList.Contains(p.WithSetMods.Parent as Protein))) { psm.TrimProteinMatches(parsimoniousProteinList); } diff --git a/MetaMorpheus/EngineLayer/ProteinScoringAndFdr/ProteinScoringAndFdrEngine.cs b/MetaMorpheus/EngineLayer/ProteinScoringAndFdr/ProteinScoringAndFdrEngine.cs index d1459cc87b..4e09455a52 100644 --- a/MetaMorpheus/EngineLayer/ProteinScoringAndFdr/ProteinScoringAndFdrEngine.cs +++ b/MetaMorpheus/EngineLayer/ProteinScoringAndFdr/ProteinScoringAndFdrEngine.cs @@ -46,7 +46,7 @@ private void ScoreProteinGroups(List proteinGroups, IEnumerable p.Peptide)) + foreach (var pepWithSetMods in psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods)) { if (!peptideToPsmMatching.TryGetValue(pepWithSetMods, out HashSet psmsForThisPeptide)) peptideToPsmMatching.Add(pepWithSetMods, new HashSet { psm }); @@ -129,14 +129,14 @@ private List DoProteinFdr(List proteinGroups) } //Do Classic protein FDR (all targets, all decoys) - // order protein groups by notch-QValue + // order protein groups by Notch-QValue var sortedProteinGroups = proteinGroups.OrderBy(b => b.BestPeptideQValue).ThenByDescending(p => p.BestPeptideScore).ToList(); AssignQValuesToProteins(sortedProteinGroups); // Do "Picked" protein FDR // adapted from "A Scalable Approach for Protein False Discovery Rate Estimation in Large Proteomic Data Sets" ~ MCP, 2015, Savitski // pair decoys and targets by accession - // then use the best peptide notch-QValue as the score for the protein group + // then use the best peptide Notch-QValue as the score for the protein group Dictionary> accessionToProteinGroup = new Dictionary>(); foreach (var pg in proteinGroups) { @@ -158,7 +158,7 @@ private List DoProteinFdr(List proteinGroups) pg.BestPeptideQValue = pg.AllPsmsBelowOnePercentFDR.Min(psm => psm.FdrInfo.QValueNotch); } - // pick the best notch-QValue for each paired accession + // pick the best Notch-QValue for each paired accession // this compares target-decoy pairs for each protein and saves the highest scoring group List rescuedProteins = new List(); foreach (var accession in accessionToProteinGroup) @@ -166,7 +166,7 @@ private List DoProteinFdr(List proteinGroups) if (accession.Value.Count > 1) { var pgList = accession.Value.OrderBy(p => p.BestPeptideQValue).ThenByDescending(p => p.BestPeptideScore).ToList(); - var pgToUse = pgList.First(); // pick lowest notch QValue and remove the rest + var pgToUse = pgList.First(); // pick lowest Notch QValue and remove the rest pgList.Remove(pgToUse); rescuedProteins.AddRange(pgList); //save the remaining protein groups proteinGroups = proteinGroups.Except(pgList).ToList(); //remove the remaining protein groups diff --git a/MetaMorpheus/EngineLayer/PsmTsv/PsmTsvWriter.cs b/MetaMorpheus/EngineLayer/PsmTsv/PsmTsvWriter.cs index 4c31717e94..ccc5ed148f 100644 --- a/MetaMorpheus/EngineLayer/PsmTsv/PsmTsvWriter.cs +++ b/MetaMorpheus/EngineLayer/PsmTsv/PsmTsvWriter.cs @@ -183,7 +183,7 @@ internal static void AddPeptideSequenceData(Dictionary s, Spectr { bool pepWithModsIsNull = sm == null || sm.BestMatchingBioPolymersWithSetMods == null || !sm.BestMatchingBioPolymersWithSetMods.Any(); - List pepsWithMods = pepWithModsIsNull ? null : sm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide).ToList(); + List pepsWithMods = pepWithModsIsNull ? null : sm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).ToList(); s[PsmTsvHeader.BaseSequence] = pepWithModsIsNull ? " " : (sm.BaseSequence ?? Resolve(pepWithModsIsNull ? null : pepsWithMods.Select(b => b.BaseSequence)).ResolvedString); s[PsmTsvHeader.FullSequence] = pepWithModsIsNull ? " " : (sm.FullSequence != null ? sm.FullSequence : Resolve(pepWithModsIsNull ? null : pepsWithMods.Select(b => b.FullSequence)).ResolvedString); diff --git a/MetaMorpheus/EngineLayer/Silac/SilacConversions.cs b/MetaMorpheus/EngineLayer/Silac/SilacConversions.cs index 42d8cff0d1..0aaf997d9a 100644 --- a/MetaMorpheus/EngineLayer/Silac/SilacConversions.cs +++ b/MetaMorpheus/EngineLayer/Silac/SilacConversions.cs @@ -7,6 +7,7 @@ using System.Linq; using Omics.Modifications; using Omics; +using EngineLayer.SpectrumMatch; namespace EngineLayer { @@ -49,16 +50,16 @@ public static PeptideSpectralMatch GetLabeledPsm(PeptideSpectralMatch psm, int n pwsm.AllModsOneIsNterminus, pwsm.NumFixedMods, labeledBaseSequence); - return psm.Clone(new List<(int Notch, IBioPolymerWithSetMods Peptide)> { (notch, labeledPwsm) }); + return psm.Clone(new List { new(notch, labeledPwsm, []) }); } public static PeptideSpectralMatch GetSilacPsm(PeptideSpectralMatch psm, SilacLabel silacLabel) { - List<(int Notch, IBioPolymerWithSetMods Peptide)> updatedBestMatchingPeptides = new List<(int Notch, IBioPolymerWithSetMods Peptide)>(); - foreach ((int Notch, PeptideWithSetModifications Peptide) notchAndPwsm in psm.BestMatchingBioPolymersWithSetMods) + List updatedBestMatchingPeptides = new(); + foreach (var notchAndPwsm in psm.BestMatchingBioPolymersWithSetMods) { - PeptideWithSetModifications modifiedPwsm = CreateSilacPwsm(silacLabel, notchAndPwsm.Peptide); - updatedBestMatchingPeptides.Add((notchAndPwsm.Notch, modifiedPwsm)); + PeptideWithSetModifications modifiedPwsm = CreateSilacPwsm(silacLabel, notchAndPwsm.WithSetMods as PeptideWithSetModifications); + updatedBestMatchingPeptides.Add(new TentativeSpectralMatch(notchAndPwsm.Notch, modifiedPwsm, notchAndPwsm.MatchedIons)); } return psm.Clone(updatedBestMatchingPeptides) as PeptideSpectralMatch; } @@ -69,11 +70,11 @@ public static List UpdateProteinSequencesToLight(List psmsToReturn = new List(); foreach (PeptideSpectralMatch psm in originalPsms) { - List<(int Notch, IBioPolymerWithSetMods Peptide)> originalPeptides = psm.BestMatchingBioPolymersWithSetMods.ToList(); - List<(int Notch, IBioPolymerWithSetMods Peptide)> updatedPeptides = new List<(int Notch, IBioPolymerWithSetMods Peptide)>(); - foreach ((int Notch, PeptideWithSetModifications Peptide) notchPwsm in originalPeptides) + List originalPeptides = psm.BestMatchingBioPolymersWithSetMods.ToList(); + List updatedPeptides = new (); + foreach (var notchPwsm in originalPeptides) { - PeptideWithSetModifications pwsm = notchPwsm.Peptide; + PeptideWithSetModifications pwsm = notchPwsm.WithSetMods as PeptideWithSetModifications; SilacLabel label = GetRelevantLabelFromBaseSequence(pwsm.BaseSequence, labels); Protein updatedProtein = pwsm.Protein; if (label != null) @@ -100,7 +101,7 @@ public static List UpdateProteinSequencesToLight(List pwsms = new(); List pwsmSpectralAngles = new(); - foreach (var (Notch, Peptide) in psms[i].BestMatchingBioPolymersWithSetMods) + foreach (var bestMatch in psms[i].BestMatchingBioPolymersWithSetMods) { //if peptide is target, directly look for the target's spectrum in the spectral library - if (!Peptide.Parent.IsDecoy && spectralLibrary.TryGetSpectrum(Peptide.FullSequence, scan.PrecursorCharge, out var librarySpectrum)) + if (!bestMatch.IsDecoy && spectralLibrary.TryGetSpectrum(bestMatch.FullSequence, scan.PrecursorCharge, out var librarySpectrum)) { SpectralSimilarity s = new SpectralSimilarity(scan.TheScan.MassSpectrum, librarySpectrum.XArray, librarySpectrum.YArray, SpectralSimilarity.SpectrumNormalizationScheme.SquareRootSpectrumSum, commonParameters.ProductMassTolerance.Value, false); if (s.SpectralContrastAngle().HasValue) { - pwsms.Add((Notch, Peptide)); pwsmSpectralAngles.Add((double)s.SpectralContrastAngle()); } } //if peptide is decoy, look for the decoy's corresponding target's spectrum in the spectral library and generate decoy spectrum by function GetDecoyLibrarySpectrumFromTargetByRevers - else if (Peptide.Parent.IsDecoy && spectralLibrary.TryGetSpectrum(Peptide.Description, scan.PrecursorCharge, out var targetlibrarySpectrum)) + else if (bestMatch.IsDecoy && spectralLibrary.TryGetSpectrum(bestMatch.WithSetMods.Description, scan.PrecursorCharge, out var targetlibrarySpectrum)) { var decoyPeptideTheorProducts = new List(); - Peptide.Fragment(commonParameters.DissociationType, commonParameters.DigestionParams.FragmentationTerminus, decoyPeptideTheorProducts); + bestMatch.WithSetMods.Fragment(commonParameters.DissociationType, commonParameters.DigestionParams.FragmentationTerminus, decoyPeptideTheorProducts); var decoylibrarySpectrum = GetDecoyLibrarySpectrumFromTargetByReverse(targetlibrarySpectrum, decoyPeptideTheorProducts); SpectralSimilarity s = new SpectralSimilarity(scan.TheScan.MassSpectrum, decoylibrarySpectrum.Select(x => x.Mz).ToArray(), decoylibrarySpectrum.Select(x => x.Intensity).ToArray(), SpectralSimilarity.SpectrumNormalizationScheme.SquareRootSpectrumSum, commonParameters.ProductMassTolerance.Value, false); if (s.SpectralContrastAngle().HasValue) { - pwsms.Add((Notch, Peptide)); pwsmSpectralAngles.Add((double)s.SpectralContrastAngle()); } } diff --git a/MetaMorpheus/EngineLayer/SpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectralMatch.cs index 6ca0ed6f0c..22243f8eea 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -9,6 +9,8 @@ using Omics; using System; using EngineLayer.CrosslinkSearch; +using EngineLayer.Util; +using EngineLayer.SpectrumMatch; namespace EngineLayer { @@ -18,7 +20,7 @@ public abstract class SpectralMatch : IComparable protected SpectralMatch(IBioPolymerWithSetMods peptide, int notch, double score, int scanIndex, Ms2ScanWithSpecificMass scan, CommonParameters commonParameters, List matchedFragmentIons, double xcorr = 0) { - _BestMatchingBioPolymersWithSetMods = new List<(int, IBioPolymerWithSetMods)>(); + _BestMatchingBioPolymersWithSetMods = new List(); ScanIndex = scanIndex; FullFilePath = scan.FullFilePath; ScanNumber = scan.OneBasedScanNumber; @@ -33,7 +35,6 @@ protected SpectralMatch(IBioPolymerWithSetMods peptide, int notch, double score, PrecursorScanEnvelopePeakCount = scan.PrecursorEnvelopePeakCount; PrecursorFractionalIntensity = scan.PrecursorFractionalIntensity; DigestionParams = commonParameters.DigestionParams; - BioPolymersWithSetModsToMatchingFragments = new Dictionary>(); Xcorr = xcorr; NativeId = scan.NativeId; RunnerUpScore = commonParameters.ScoreCutoff; @@ -108,7 +109,7 @@ public List PrecursorMassErrorDa { get { - return this._BestMatchingBioPolymersWithSetMods.Select(p => Math.Round(this.ScanPrecursorMass - p.Pwsm.MonoisotopicMass, 5)) + return this._BestMatchingBioPolymersWithSetMods.Select(p => Math.Round(this.ScanPrecursorMass - p.WithSetMods.MonoisotopicMass, 5)) .ToList(); } } @@ -117,32 +118,25 @@ public List PrecursorMassErrorPpm { get { - return this._BestMatchingBioPolymersWithSetMods.Select(p => Math.Round((this.ScanPrecursorMass - p.Pwsm.MonoisotopicMass) / p.Pwsm.MonoisotopicMass * 1e6, 2)).ToList(); + return this._BestMatchingBioPolymersWithSetMods.Select(p => Math.Round((this.ScanPrecursorMass - p.WithSetMods.MonoisotopicMass) / p.WithSetMods.MonoisotopicMass * 1e6, 2)).ToList(); } } #region Search public DigestionParams DigestionParams { get; } - public static BioPolymerNotchFragmentIonComparer<(int notch, IBioPolymerWithSetMods pwsm, List ions)> BioPolymerNotchFragmentIonComparer = new(); + public static BioPolymerNotchFragmentIonComparer BioPolymerNotchFragmentIonComparer = new(); + protected List _BestMatchingBioPolymersWithSetMods; - // TODO: The BioPolymerWithSetModsToMatchingFragments dictionary should be more tightly coupled to the _BestMatchingBioPolymersWithSetMods list, - // so that the two are always in sync. This would make the code more robust and easier to understand. - public Dictionary> BioPolymersWithSetModsToMatchingFragments { get; private set; } - - protected List<(int Notch, IBioPolymerWithSetMods Pwsm)> _BestMatchingBioPolymersWithSetMods; - - public IEnumerable<(int Notch, IBioPolymerWithSetMods Peptide)> BestMatchingBioPolymersWithSetMods + public IEnumerable BestMatchingBioPolymersWithSetMods { get { // This property gets called frequently // It might be worth considering stashing the sorted list in a field instead of sorting every time - // Order by descending sorts things from high (better matches) to low (worse matches) - return _BestMatchingBioPolymersWithSetMods.OrderByDescending(t => - (t.Notch, t.Pwsm, BioPolymersWithSetModsToMatchingFragments.TryGetValue(t.Pwsm, out var ions) ? ions : null), - comparer: BioPolymerNotchFragmentIonComparer); + // Order high (better matches) to low (worse matches) + return _BestMatchingBioPolymersWithSetMods.OrderBy(p => p, BioPolymerNotchFragmentIonComparer); } } @@ -151,7 +145,7 @@ public void AddOrReplace(IBioPolymerWithSetMods pwsm, double newScore, int notch if (newScore - Score > ToleranceForScoreDifferentiation) //if new score beat the old score, overwrite it { _BestMatchingBioPolymersWithSetMods.Clear(); - _BestMatchingBioPolymersWithSetMods.Add((notch, pwsm)); + _BestMatchingBioPolymersWithSetMods.Add(new(notch, pwsm, matchedFragmentIons)); if (Score - RunnerUpScore > ToleranceForScoreDifferentiation) { @@ -159,14 +153,10 @@ public void AddOrReplace(IBioPolymerWithSetMods pwsm, double newScore, int notch } Score = newScore; Xcorr = newXcorr; - - BioPolymersWithSetModsToMatchingFragments.Clear(); - BioPolymersWithSetModsToMatchingFragments.Add(pwsm, matchedFragmentIons); } else if (newScore - Score > -ToleranceForScoreDifferentiation && reportAllAmbiguity) //else if the same score and ambiguity is allowed { - _BestMatchingBioPolymersWithSetMods.Add((notch, pwsm)); - BioPolymersWithSetModsToMatchingFragments.TryAdd(pwsm, matchedFragmentIons); + _BestMatchingBioPolymersWithSetMods.Add(new(notch, pwsm, matchedFragmentIons)); } else if (newScore - RunnerUpScore > ToleranceForScoreDifferentiation) { @@ -175,13 +165,9 @@ public void AddOrReplace(IBioPolymerWithSetMods pwsm, double newScore, int notch } //PEP-Value analysis identifies ambiguous peptides with lower probability. These are removed from the bestmatchingpeptides dictionary, which lowers ambiguity. - public void RemoveThisAmbiguousPeptide(int notch, IBioPolymerWithSetMods pwsm) + public void RemoveThisAmbiguousPeptide(TentativeSpectralMatch tentativeSpectralMatch) { - _BestMatchingBioPolymersWithSetMods.Remove((notch, pwsm)); - if (!_BestMatchingBioPolymersWithSetMods.Any(x => x.Pwsm.Equals(pwsm))) - { - BioPolymersWithSetModsToMatchingFragments.Remove(pwsm); - } + _BestMatchingBioPolymersWithSetMods.Remove(tentativeSpectralMatch); this.ResolveAllAmbiguities(); } @@ -195,40 +181,34 @@ public void ResolveAllAmbiguities() // Order the BPWSM list for stability _BestMatchingBioPolymersWithSetMods = BestMatchingBioPolymersWithSetMods.ToList(); - IsDecoy = _BestMatchingBioPolymersWithSetMods.Any(p => p.Pwsm.Parent.IsDecoy); - IsContaminant = _BestMatchingBioPolymersWithSetMods.Any(p => p.Pwsm.Parent.IsContaminant); - FullSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.FullSequence)).ResolvedValue; - BaseSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.BaseSequence)).ResolvedValue; - BioPolymerWithSetModsLength = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.Length)).ResolvedValue; - OneBasedStartResidue = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.OneBasedStartResidue)).ResolvedValue; - OneBasedEndResidue = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.OneBasedEndResidue)).ResolvedValue; - ParentLength = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.Parent.Length)).ResolvedValue; - BioPolymerWithSetModsMonoisotopicMass = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.MonoisotopicMass)).ResolvedValue; - Accession = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.Parent.Accession)).ResolvedValue; - Organism = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.Parent.Organism)).ResolvedValue; - ModsIdentified = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.AllModsOneIsNterminus)).ResolvedValue; - ModsChemicalFormula = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Pwsm.AllModsOneIsNterminus.Select(c => (c.Value)))).ResolvedValue; + IsDecoy = _BestMatchingBioPolymersWithSetMods.Any(p => p.WithSetMods.Parent.IsDecoy); + IsContaminant = _BestMatchingBioPolymersWithSetMods.Any(p => p.WithSetMods.Parent.IsContaminant); + FullSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.FullSequence)).ResolvedValue; + BaseSequence = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.BaseSequence)).ResolvedValue; + BioPolymerWithSetModsLength = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.Length)).ResolvedValue; + OneBasedStartResidue = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.OneBasedStartResidue)).ResolvedValue; + OneBasedEndResidue = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.OneBasedEndResidue)).ResolvedValue; + ParentLength = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.Parent.Length)).ResolvedValue; + BioPolymerWithSetModsMonoisotopicMass = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.MonoisotopicMass)).ResolvedValue; + Accession = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.Parent.Accession)).ResolvedValue; + Organism = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.Parent.Organism)).ResolvedValue; + ModsIdentified = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.AllModsOneIsNterminus)).ResolvedValue; + ModsChemicalFormula = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.AllModsOneIsNterminus.Select(c => (c.Value)))).ResolvedValue; Notch = PsmTsvWriter.Resolve(_BestMatchingBioPolymersWithSetMods.Select(b => b.Notch)).ResolvedValue; //if the PSM matches a target and a decoy and they are the SAME SEQUENCE, remove the decoy if (IsDecoy) { bool removedPeptides = false; - var hits = _BestMatchingBioPolymersWithSetMods.GroupBy(p => p.Pwsm.FullSequence); + var hits = _BestMatchingBioPolymersWithSetMods.GroupBy(p => p.FullSequence); foreach (var hit in hits) { - if (hit.Any(p => p.Pwsm.Parent.IsDecoy) && hit.Any(p => !p.Pwsm.Parent.IsDecoy)) + if (hit.Any(p => p.WithSetMods.Parent.IsDecoy) && hit.Any(p => !p.IsDecoy)) { // at least one peptide with this sequence is a target and at least one is a decoy // remove the decoys with this sequence - var pwsmToRemove = _BestMatchingBioPolymersWithSetMods.Where(p => p.Pwsm.FullSequence == hit.Key && p.Pwsm.Parent.IsDecoy).ToList(); - _BestMatchingBioPolymersWithSetMods.RemoveAll(p => p.Pwsm.FullSequence == hit.Key && p.Pwsm.Parent.IsDecoy); - foreach ((int, IBioPolymerWithSetMods) pwsm in pwsmToRemove) - { - BioPolymersWithSetModsToMatchingFragments.Remove(pwsm.Item2); - } - + _BestMatchingBioPolymersWithSetMods.RemoveAll(p => p.FullSequence == hit.Key && p.IsDecoy); removedPeptides = true; } } @@ -243,10 +223,9 @@ public void ResolveAllAmbiguities() // However, writing out all the matched ions for all the peptide options would break excel // Instead, we set MatchedFragmentIons as the ions matched to the best peptide option if (this is CrosslinkSpectralMatch) // CrosslinkSpectralMatch has its own way of handling this, however, this method of retrieving the "First" item in a dictionary is problematic and should be revisted at some point - MatchedFragmentIons = BioPolymersWithSetModsToMatchingFragments.Values.First(); - else if (BioPolymersWithSetModsToMatchingFragments.TryGetValue(_BestMatchingBioPolymersWithSetMods.First().Pwsm, out var ionList)) - MatchedFragmentIons = ionList; - else MatchedFragmentIons = null; + MatchedFragmentIons = _BestMatchingBioPolymersWithSetMods.First().MatchedIons; + else + MatchedFragmentIons = _BestMatchingBioPolymersWithSetMods.First().MatchedIons; } public void SetFdrValues(double cumulativeTarget, double cumulativeDecoy, double qValue, double cumulativeTargetNotch, double cumulativeDecoyNotch, double qValueNotch, double pep, double pepQValue) @@ -323,15 +302,15 @@ public void TrimProteinMatches(HashSet parsimoniousProteins) { if (IsDecoy) { - if (_BestMatchingBioPolymersWithSetMods.Any(p => parsimoniousProteins.Contains(p.Pwsm.Parent) && p.Pwsm.Parent.IsDecoy)) + if (_BestMatchingBioPolymersWithSetMods.Any(p => parsimoniousProteins.Contains(p.WithSetMods.Parent) && p.WithSetMods.Parent.IsDecoy)) { - _BestMatchingBioPolymersWithSetMods.RemoveAll(p => !parsimoniousProteins.Contains(p.Pwsm.Parent)); + _BestMatchingBioPolymersWithSetMods.RemoveAll(p => !parsimoniousProteins.Contains(p.WithSetMods.Parent)); } // else do nothing } else { - _BestMatchingBioPolymersWithSetMods.RemoveAll(p => !parsimoniousProteins.Contains(p.Pwsm.Parent)); + _BestMatchingBioPolymersWithSetMods.RemoveAll(p => !parsimoniousProteins.Contains(p.WithSetMods.Parent)); } ResolveAllAmbiguities(); @@ -340,15 +319,11 @@ public void TrimProteinMatches(HashSet parsimoniousProteins) /// /// This method is used by protein parsimony to add PeptideWithSetModifications objects for modification-agnostic parsimony /// - public void AddProteinMatch((int, IBioPolymerWithSetMods) peptideWithNotch, List mfi) + public void AddProteinMatch(TentativeSpectralMatch tentativeSpectralMatch) { - if (!_BestMatchingBioPolymersWithSetMods.Select(p => p.Pwsm).Contains(peptideWithNotch.Item2)) + if (!_BestMatchingBioPolymersWithSetMods.Contains(tentativeSpectralMatch)) { - _BestMatchingBioPolymersWithSetMods.Add(peptideWithNotch); - if (!BioPolymersWithSetModsToMatchingFragments.ContainsKey(peptideWithNotch.Item2)) - { - BioPolymersWithSetModsToMatchingFragments.Add(peptideWithNotch.Item2, mfi); - } + _BestMatchingBioPolymersWithSetMods.Add(tentativeSpectralMatch); ResolveAllAmbiguities(); } } @@ -357,11 +332,11 @@ public void AddProteinMatch((int, IBioPolymerWithSetMods) peptideWithNotch, List #region Silac - protected SpectralMatch(SpectralMatch psm, List<(int Notch, IBioPolymerWithSetMods Peptide)> bestMatchingPeptides) + protected SpectralMatch(SpectralMatch psm, List bestMatchingPeptides) { _BestMatchingBioPolymersWithSetMods = bestMatchingPeptides; - BaseSequence = PsmTsvWriter.Resolve(bestMatchingPeptides.Select(b => b.Peptide.BaseSequence)).ResolvedValue; - FullSequence = PsmTsvWriter.Resolve(bestMatchingPeptides.Select(b => b.Peptide.FullSequence)).ResolvedValue; + BaseSequence = PsmTsvWriter.Resolve(bestMatchingPeptides.Select(b => b.WithSetMods.BaseSequence)).ResolvedValue; + FullSequence = PsmTsvWriter.Resolve(bestMatchingPeptides.Select(b => b.WithSetMods.FullSequence)).ResolvedValue; ModsChemicalFormula = psm.ModsChemicalFormula; Notch = psm.Notch; @@ -393,7 +368,6 @@ protected SpectralMatch(SpectralMatch psm, List<(int Notch, IBioPolymerWithSetMo IsDecoy = psm.IsDecoy; IsContaminant = psm.IsContaminant; DigestionParams = psm.DigestionParams; - BioPolymersWithSetModsToMatchingFragments = psm.BioPolymersWithSetModsToMatchingFragments; SpectralAngle = psm.SpectralAngle; } @@ -410,10 +384,10 @@ protected SpectralMatch(SpectralMatch psm, List<(int Notch, IBioPolymerWithSetMo /// /// /// - public static int GetLongestIonSeriesBidirectional(Dictionary> PeptidesToMatchingFragments, IBioPolymerWithSetMods peptide) + public static int GetLongestIonSeriesBidirectional(List matchedFragments, IBioPolymerWithSetMods peptide) { List maxDiffs = new List { 1 }; - if (PeptidesToMatchingFragments != null && PeptidesToMatchingFragments.TryGetValue(peptide, out var matchedFragments) && matchedFragments != null && matchedFragments.Any()) + if (matchedFragments != null && matchedFragments.Count != 0) { var jointSeries = matchedFragments.Select(p => p.NeutralTheoreticalProduct.AminoAcidPosition).Distinct().ToList(); @@ -540,9 +514,9 @@ public void GetAminoAcidCoverage() this.FragmentCoveragePositionInPeptide = fragmentCoveredAminoAcidsList; } - public static int GetCountComplementaryIons(Dictionary> PeptidesToMatchingFragments, IBioPolymerWithSetMods peptide) + public static int GetCountComplementaryIons(List matchedFragments, IBioPolymerWithSetMods peptide) { - if (PeptidesToMatchingFragments != null && PeptidesToMatchingFragments.TryGetValue(peptide, out var matchedFragments) && matchedFragments != null && matchedFragments.Any()) + if (matchedFragments != null && matchedFragments.Count != 0) { List nIons = matchedFragments.Where(f => f.NeutralTheoreticalProduct.Terminus == FragmentationTerminus.N).Select(f => f.NeutralTheoreticalProduct.FragmentNumber).ToList(); List cIons = matchedFragments.Where(f => f.NeutralTheoreticalProduct.Terminus == FragmentationTerminus.C).Select(f => (peptide.BaseSequence.Length - f.NeutralTheoreticalProduct.FragmentNumber)).ToList(); diff --git a/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs new file mode 100644 index 0000000000..c10773dea0 --- /dev/null +++ b/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs @@ -0,0 +1,51 @@ +using Omics; +using Omics.Fragmentation; +using System.Collections.Generic; + +namespace EngineLayer.SpectrumMatch; + +public class BioPolymerNotchFragmentIonComparer : Comparer<(int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons)>, + IComparer +{ + /// + /// Returns less than 0 if x is better than y, greater than 0 if y is better than x, and 0 if they are equal. + /// Better is defined as having a lower Notch, more fragment ions, or a shorter sequence (i.e. fewer modifications) in that order. + /// If the aforementioned criteria are equal, then the two are compared based on the alphebetical ordering of the full sequence + /// + public override int Compare((int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons) x, (int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons) y) + { + if (x.Notch != y.Notch) + return x.Notch.CompareTo(y.Notch); // Lower Notch is better + + if (x.MatchedIons?.Count != y.MatchedIons?.Count && !ReferenceEquals(x.MatchedIons, null)) + return -1 * x.MatchedIons.Count.CompareTo(y.MatchedIons?.Count); // More ions are better + + if (x.Bpwsm == null && y.Bpwsm == null) + return 0; + if (x.Bpwsm == null) + return 1; // Null Bpwsm is considered worse + if (y.Bpwsm == null) + return -1; // Null Bpwsm is considered worse + + if (x.Bpwsm.NumMods != y.Bpwsm.NumMods) + return x.Bpwsm.NumMods.CompareTo(y.Bpwsm.NumMods); // Fewer mods are better + + if (x.Bpwsm.FullSequence != y.Bpwsm.FullSequence) + return string.Compare(x.Bpwsm.FullSequence, y.Bpwsm.FullSequence); // Alphabetical ordering of full sequence + + if (x.Bpwsm.Parent?.Accession != y.Bpwsm.Parent?.Accession) // This will break if the protein accession is not set (I'm not sure if that's possible) + return string.Compare(x.Bpwsm.Parent?.Accession, y.Bpwsm.Parent?.Accession); // Alphabetical ordering of protein accession + + return x.Bpwsm.OneBasedStartResidue.CompareTo(y.Bpwsm.OneBasedStartResidue); + } + + public int Compare(TentativeSpectralMatch x, TentativeSpectralMatch y) + { + if (x is null && y is null) + return 0; + if (x is null) return 1; + if (y is null) return -1; + + return Compare((x.Notch, x.WithSetMods, x.MatchedIons), (y.Notch, y.WithSetMods, y.MatchedIons)); + } +} \ No newline at end of file diff --git a/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs new file mode 100644 index 0000000000..93b06deda9 --- /dev/null +++ b/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs @@ -0,0 +1,43 @@ +#nullable enable +using Omics; +using System.Collections.Generic; +using Omics.Fragmentation; +using System; + +namespace EngineLayer.SpectrumMatch; + +/// +/// Class to represent a possible spectral match +/// +/// +/// +/// +public class TentativeSpectralMatch(int notch, IBioPolymerWithSetMods pwsm, List matchedIons) : IEquatable +{ + public readonly int Notch = notch; + public readonly IBioPolymerWithSetMods WithSetMods = pwsm; + public readonly List MatchedIons = matchedIons; + + public bool IsDecoy => WithSetMods.Parent.IsDecoy; + public string FullSequence => WithSetMods.FullSequence; + + public bool Equals(TentativeSpectralMatch? other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return Notch == other.Notch && WithSetMods.Equals(other.WithSetMods) && Equals(MatchedIons.Count, other.MatchedIons.Count); + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((TentativeSpectralMatch)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(Notch, WithSetMods, MatchedIons); + } +} \ No newline at end of file diff --git a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs new file mode 100644 index 0000000000..c61cd9babe --- /dev/null +++ b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs @@ -0,0 +1,144 @@ +#nullable enable +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace EngineLayer.Util; + +/// +/// Represents a priority queue with a fixed maximum capacity. +/// The queue maintains DISTINCT elements in sorted order based on their priority then by the comparer. +/// Default comparer compares priority, then a CompareTo if T is of type IComparable, then hash code of T and ranks in descending order. +/// +/// The type of elements in the priority queue. +public class PriorityQueue : IEnumerable +{ + private readonly int _maxCapacity; + protected readonly SortedSet<(double, T)> SortedSet; + protected readonly IComparer<(double, T)> Comparer; + protected readonly IComparer InternalComparer; + + /// + /// Gets the number of elements in the priority queue. + /// + public int Count => SortedSet.Count; + + /// + /// Constructs a priority queue with a maximum capacity + /// + /// Maximum number of results to keep + /// Default comparer compares priority then hash code of T + public PriorityQueue(int maxCapacity = 128, IComparer? comparer = null) + { + InternalComparer = comparer ?? Comparer.Default; + Comparer = Comparer<(double, T)>.Create((x, y) => + { + int priorityComparison = x.Item1.CompareTo(y.Item1); + if (priorityComparison != 0) + return -priorityComparison; // higher priority is better + if (x.Item2 is null && y.Item2 is null) + return 0; + if (x.Item2 is null) + return 1; + if (y.Item2 is null) + return 1; + return InternalComparer.Compare(x.Item2, y.Item2); + }); + SortedSet = new SortedSet<(double, T)>(Comparer); + _maxCapacity = maxCapacity; + } + + /// + /// Adds an element to the priority queue with a specified priority. + /// If the queue is at maximum capacity, the element with the lowest priority is removed. + /// + /// The priority of the element to be added. + /// The element to be added to the queue. + public void Enqueue(double priority, T item) + { + if (SortedSet.Count >= _maxCapacity) + { + // Remove the item with the lowest priority (last item) if the queue is at max capacity + SortedSet.Remove(SortedSet.Max); + } + SortedSet.Add((priority, item)); + } + + /// + /// Removes and returns the element with the highest priority from the queue. + /// + /// The element with the highest priority, or the default if queue is empty. + public T? Dequeue() + { + var highestPriority = SortedSet.Min; // if queue is empty, max will be the default anonymous type (0, null) + if (EqualityComparer<(double, T)>.Default.Equals(highestPriority, default((double, T)))) + return default; + + SortedSet.Remove(highestPriority); + return highestPriority.Item2; + } + + /// + /// Removes and returns the element with the highest priority from the queue. + /// + /// The element with the highest priority and its priority, or default if the queue is empty. + public (double, T)? DequeueWithPriority() + { + var highestPriority = SortedSet.Min; + // if queue is empty, max will be the default anonymous type (0, null) + if (EqualityComparer<(double, T)>.Default.Equals(highestPriority, default((double, T)))) + return default; + + SortedSet.Remove(highestPriority); + return highestPriority; + } + + /// + /// Returns the element with the highest priority without removing it from the queue. + /// + /// The element with the highest priority, or null if the queue is empty. + public T? Peek() + { + return SortedSet.Min.Item2; + } + + public void Remove(T item) + { + if (item is null) + return; + + foreach (var element in SortedSet) + { + if (InternalComparer.Compare(element.Item2, item) != 0) + continue; + + SortedSet.Remove(element); + return; + } + } + + public List ToList() => SortedSet.Select(x => x.Item2).ToList(); + + public List<(double, T)> ToListWithPriority() => SortedSet.ToList(); + + public T[] ToArray() => SortedSet.Select(x => x.Item2).ToArray(); + + public (double, T)[] ToArrayWithPriority() => SortedSet.ToArray(); + + /// + /// Returns an enumerator that iterates through the elements of the priority queue in priority order. + /// + /// An enumerator for the priority queue. + public IEnumerator GetEnumerator() + { + foreach (var item in SortedSet) + { + yield return item.Item2; + } + } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } +} \ No newline at end of file diff --git a/MetaMorpheus/TaskLayer/GlycoSearchTask/PostGlycoSearchAnalysisTask.cs b/MetaMorpheus/TaskLayer/GlycoSearchTask/PostGlycoSearchAnalysisTask.cs index a63750858a..504aa9a54b 100644 --- a/MetaMorpheus/TaskLayer/GlycoSearchTask/PostGlycoSearchAnalysisTask.cs +++ b/MetaMorpheus/TaskLayer/GlycoSearchTask/PostGlycoSearchAnalysisTask.cs @@ -321,9 +321,9 @@ private void GlycoAccessionAnalysis(List gsms, string indivi { foreach (var psm in _filteredPsms) { - List proteinList = psm.BestMatchingBioPolymersWithSetMods.Select(p => ((PeptideWithSetModifications)p.Peptide).Protein).ToList(); + List proteinList = psm.BestMatchingBioPolymersWithSetMods.Select(p => ((PeptideWithSetModifications)p.WithSetMods).Protein).ToList(); ProteinGroup newProteinGroup = new ProteinGroup(new HashSet(proteinList), - new HashSet(new List(psm.BestMatchingBioPolymersWithSetMods.Select(p=> (PeptideWithSetModifications)p.Peptide).ToList())), new HashSet()); + new HashSet(new List(psm.BestMatchingBioPolymersWithSetMods.Select(p=> (PeptideWithSetModifications)p.WithSetMods).ToList())), new HashSet()); if (_proteinGroups.Any(p => p.Equals(newProteinGroup))) { @@ -454,7 +454,7 @@ private void QuantificationAnalysis() var accessionToPg = new Dictionary(); foreach (var psm in unambiguousPsmsBelowOnePercentFdr) { - var proteins = psm.BestMatchingBioPolymersWithSetMods.Select(b => ((PeptideWithSetModifications)b.Peptide).Protein).Distinct(); + var proteins = psm.BestMatchingBioPolymersWithSetMods.Select(b => ((PeptideWithSetModifications)b.WithSetMods).Protein).Distinct(); foreach (var protein in proteins) { diff --git a/MetaMorpheus/TaskLayer/MbrAnalysis/SpectralRecoveryRunner.cs b/MetaMorpheus/TaskLayer/MbrAnalysis/SpectralRecoveryRunner.cs index 22de2c9203..0039ce2fca 100644 --- a/MetaMorpheus/TaskLayer/MbrAnalysis/SpectralRecoveryRunner.cs +++ b/MetaMorpheus/TaskLayer/MbrAnalysis/SpectralRecoveryRunner.cs @@ -94,7 +94,7 @@ public static SpectralRecoveryResults RunSpectralRecoveryAlgorithm( { break; } - IBioPolymerWithSetMods bestDonorPwsm = bestDonorPsm.BestMatchingBioPolymersWithSetMods.First().Peptide; + IBioPolymerWithSetMods bestDonorPwsm = bestDonorPsm.BestMatchingBioPolymersWithSetMods.First().WithSetMods; IEnumerable peptideSpectralMatches = mcse.SearchAroundPeak(bestDonorPwsm, mbrPeak.Apex.IndexedPeak.RetentionTime); diff --git a/MetaMorpheus/TaskLayer/PepXMLWriter.cs b/MetaMorpheus/TaskLayer/PepXMLWriter.cs index 3b30ab068d..d831983574 100644 --- a/MetaMorpheus/TaskLayer/PepXMLWriter.cs +++ b/MetaMorpheus/TaskLayer/PepXMLWriter.cs @@ -119,7 +119,7 @@ public static void WritePepXml(List psms, List databas foreach (var psm in psms) { - PeptideWithSetModifications peptide = psm.BestMatchingBioPolymersWithSetMods.First().Peptide as PeptideWithSetModifications; + PeptideWithSetModifications peptide = psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods as PeptideWithSetModifications; var mods = new List(); foreach (var mod in peptide.AllModsOneIsNterminus) @@ -132,7 +132,7 @@ public static void WritePepXml(List psms, List databas mods.Add(pepXmlMod); } - var proteinAccessions = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide.Parent.Accession).Distinct(); + var proteinAccessions = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods.Parent.Accession).Distinct(); var searchHit = new pepXML.Generated.msms_pipeline_analysisMsms_run_summarySpectrum_querySearch_resultSearch_hit { diff --git a/MetaMorpheus/TaskLayer/SearchTask/MzIdentMLWriter.cs b/MetaMorpheus/TaskLayer/SearchTask/MzIdentMLWriter.cs index 4b842483da..98a3c29af7 100644 --- a/MetaMorpheus/TaskLayer/SearchTask/MzIdentMLWriter.cs +++ b/MetaMorpheus/TaskLayer/SearchTask/MzIdentMLWriter.cs @@ -40,7 +40,7 @@ public static void WriteMzIdentMl(IEnumerable psms, List p.BaseSequence != null && !p.FullSequence.Contains("|") && !labelsToSearch.Any(x => p.BaseSequence.Contains(x))); } - List peptides = psms.SelectMany(i => i.BestMatchingBioPolymersWithSetMods.Select(v => v.Peptide as PeptideWithSetModifications)).Distinct().ToList(); + List peptides = psms.SelectMany(i => i.BestMatchingBioPolymersWithSetMods.Select(v => v.WithSetMods as PeptideWithSetModifications)).Distinct().ToList(); List proteins = peptides.Select(p => p.Protein).Distinct().ToList(); List filenames = psms.Select(i => i.FullFilePath).Distinct().ToList(); Dictionary database_reference = new Dictionary(); @@ -327,7 +327,7 @@ public static void WriteMzIdentMl(IEnumerable psms, List p.Peptide).Distinct()) + foreach (PeptideWithSetModifications peptide in psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).Distinct()) { //if first peptide on list hasn't been added, add peptide and peptide evidence if (!peptide_ids.TryGetValue(peptide.FullSequence, out Tuple> peptide_id)) @@ -408,7 +408,7 @@ public static void WriteMzIdentMl(IEnumerable psms, List(psm.FullFilePath, psm.ScanNumber)] = new Tuple(scan_result_scan_item.Item1, scan_result_scan_item.Item2 + 1); scan_result_scan_item = psm_per_scan[new Tuple(psm.FullFilePath, psm.ScanNumber)]; } - foreach (PeptideWithSetModifications p in psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide).Distinct()) + foreach (PeptideWithSetModifications p in psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).Distinct()) { peptide_ids[p.FullSequence].Item2.Add("SII_" + scan_result_scan_item.Item1 + "_" + scan_result_scan_item.Item2); } @@ -421,7 +421,7 @@ public static void WriteMzIdentMl(IEnumerable psms, List p.Peptide).Distinct().Count()], + PeptideEvidenceRef = new mzIdentML110.Generated.PeptideEvidenceRefType[psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).Distinct().Count()], cvParam = new mzIdentML110.Generated.CVParamType[2] { new mzIdentML110.Generated.CVParamType @@ -447,7 +447,7 @@ public static void WriteMzIdentMl(IEnumerable psms, List p.Peptide).Distinct()) + foreach (PeptideWithSetModifications p in psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).Distinct()) { _mzid.DataCollection.AnalysisData.SpectrumIdentificationList[0].SpectrumIdentificationResult[scan_result_scan_item.Item1].SpectrumIdentificationItem[scan_result_scan_item.Item2].PeptideEvidenceRef[pe] = new mzIdentML110.Generated.PeptideEvidenceRefType diff --git a/MetaMorpheus/TaskLayer/SearchTask/PostSearchAnalysisTask.cs b/MetaMorpheus/TaskLayer/SearchTask/PostSearchAnalysisTask.cs index e66b1813c9..a7c351bcf5 100644 --- a/MetaMorpheus/TaskLayer/SearchTask/PostSearchAnalysisTask.cs +++ b/MetaMorpheus/TaskLayer/SearchTask/PostSearchAnalysisTask.cs @@ -335,7 +335,7 @@ private void QuantificationAnalysis() var accessionToPg = new Dictionary(); foreach (var psm in psmsForQuantification) { - var proteins = psm.BestMatchingBioPolymersWithSetMods.Select(b => b.Peptide.Parent).Distinct(); + var proteins = psm.BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.Parent).Distinct(); foreach (var protein in proteins) { @@ -387,7 +387,7 @@ private void QuantificationAnalysis() //get easy access to values we need for new psm generation string unlabeledBaseSequence = lightPsm.BaseSequence; int notch = psm.BestMatchingBioPolymersWithSetMods.First().Notch; - PeptideWithSetModifications pwsm = psm.BestMatchingBioPolymersWithSetMods.First().Peptide as PeptideWithSetModifications; + PeptideWithSetModifications pwsm = psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods as PeptideWithSetModifications; //check if turnover or multiplex experiment if (startLabel == null && endLabel == null) //if multiplex @@ -612,7 +612,7 @@ protected void WritePsmsToTsv(IEnumerable psms, string filePath, if (Parameters.SearchParameters.DoMultiplexQuantification && Parameters.MultiplexModification != null && psms.Any(p => p.BestMatchingBioPolymersWithSetMods - .SelectMany(pwsm => pwsm.Peptide.AllModsOneIsNterminus.Values) + .SelectMany(pwsm => pwsm.WithSetMods.AllModsOneIsNterminus.Values) .Any(mod => mod.OriginalId.Equals(Parameters.MultiplexModification.OriginalId)))) { WritePsmPlusMultiplexIons(psms, filePath); @@ -1063,7 +1063,7 @@ private void WritePrunedDatabase() // associate all confident PSMs with all possible proteins they could be digest products of (before or after parsimony) foreach (SpectralMatch psm in filteredPsms) { - var myPepsWithSetMods = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide); + var myPepsWithSetMods = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods); foreach (PeptideWithSetModifications peptide in myPepsWithSetMods) { @@ -1116,7 +1116,7 @@ private void WritePrunedDatabase() foreach (SpectralMatch psm in originalModPsms) { - var myPepsWithSetMods = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide); + var myPepsWithSetMods = psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods); foreach (PeptideWithSetModifications peptide in myPepsWithSetMods) { @@ -1449,7 +1449,7 @@ private void WriteVariantResults() filterAtPeptideLevel: true); var possibleVariantPsms = fdrPsms.Where(p => - p.BestMatchingBioPolymersWithSetMods.Any(pep => pep.Peptide is PeptideWithSetModifications pwsm && pwsm.IsVariantPeptide())) + p.BestMatchingBioPolymersWithSetMods.Any(pep => pep.WithSetMods is PeptideWithSetModifications pwsm && pwsm.IsVariantPeptide())) .OrderByDescending(pep => pep.Score) .ToList(); @@ -1481,7 +1481,7 @@ private void WriteVariantResults() foreach (var entry in variantPeptides) { var pwsm = entry.BestMatchingBioPolymersWithSetMods; - var nonVariantOption = pwsm.Any(p => p.Peptide is PeptideWithSetModifications pwsm && pwsm.IsVariantPeptide() == false); + var nonVariantOption = pwsm.Any(p => p.WithSetMods is PeptideWithSetModifications pwsm && pwsm.IsVariantPeptide() == false); if (nonVariantOption == false) { confidentVariantPeps.Add(entry); @@ -1520,7 +1520,7 @@ private void WriteVariantResults() List modifiedVariantSitePeptides = new();// modification is speciifcally on the variant residue within the peptide foreach (PeptideSpectralMatch entry in modifiedVariantPeptides) { - PeptideWithSetModifications firstOrDefault = entry.BestMatchingBioPolymersWithSetMods.FirstOrDefault().Peptide as PeptideWithSetModifications; + PeptideWithSetModifications firstOrDefault = entry.BestMatchingBioPolymersWithSetMods.FirstOrDefault().WithSetMods as PeptideWithSetModifications; var variantPWSM = firstOrDefault; var peptideMods = variantPWSM.AllModsOneIsNterminus.Values.ToList(); @@ -1545,11 +1545,10 @@ private void WriteVariantResults() foreach (var peptide in confidentVariantPeps) { var variantPWSM = - peptide.BestMatchingBioPolymersWithSetMods.FirstOrDefault() is (_, PeptideWithSetModifications) - ? ((int Notch, PeptideWithSetModifications Peptide))peptide.BestMatchingBioPolymersWithSetMods - .FirstOrDefault() - : (0, null);//TODO: expand to all peptide options not just the first - var variants = variantPWSM.Peptide.Protein.AppliedSequenceVariations; + peptide.BestMatchingBioPolymersWithSetMods.FirstOrDefault()?.WithSetMods is PeptideWithSetModifications peptideWithSetModifications + ? peptideWithSetModifications + : null;//TODO: expand to all peptide options not just the first + var variants = variantPWSM.Protein.AppliedSequenceVariations; var culture = CultureInfo.CurrentCulture; // these bools allow for us to accurrately count the number of peptides that have at least one variants of a given type. // they will prevent double counting if a variant type is found more than once in a given peptide (most typically missense, but all will be covered) @@ -1563,7 +1562,7 @@ private void WriteVariantResults() foreach (var variant in variants) { - if (variantPWSM.Peptide.IntersectsAndIdentifiesVariation(variant).identifies == true) + if (variantPWSM.IntersectsAndIdentifiesVariation(variant).identifies == true) { if (culture.CompareInfo.IndexOf(variant.Description.Description, "missense_variant", CompareOptions.IgnoreCase) >= 0) { @@ -1574,13 +1573,13 @@ private void WriteVariantResults() SNVmissenseCount++; SNVmissenseIdentified = true; } - if (SNVmissenseVariants.ContainsKey(variantPWSM.Peptide.Protein)) + if (SNVmissenseVariants.ContainsKey(variantPWSM.Protein)) { - SNVmissenseVariants[variantPWSM.Peptide.Protein].Add(variant); + SNVmissenseVariants[variantPWSM.Protein].Add(variant); } else { - SNVmissenseVariants.Add(variantPWSM.Peptide.Protein, new HashSet { variant }); + SNVmissenseVariants.Add(variantPWSM.Protein, new HashSet { variant }); } } else @@ -1590,13 +1589,13 @@ private void WriteVariantResults() MNVmissenseCount++; MNVmissenseIdentified = true; } - if (MNVmissenseVariants.ContainsKey(variantPWSM.Peptide.Protein)) + if (MNVmissenseVariants.ContainsKey(variantPWSM.Protein)) { - MNVmissenseVariants[variantPWSM.Peptide.Protein].Add(variant); + MNVmissenseVariants[variantPWSM.Protein].Add(variant); } else { - MNVmissenseVariants.Add(variantPWSM.Peptide.Protein, new HashSet { variant }); + MNVmissenseVariants.Add(variantPWSM.Protein, new HashSet { variant }); } } } @@ -1607,13 +1606,13 @@ private void WriteVariantResults() frameshiftCount++; frameshiftIdentified = true; } - if (frameshiftVariants.ContainsKey(variantPWSM.Peptide.Protein)) + if (frameshiftVariants.ContainsKey(variantPWSM.Protein)) { - frameshiftVariants[variantPWSM.Peptide.Protein].Add(variant); + frameshiftVariants[variantPWSM.Protein].Add(variant); } else { - frameshiftVariants.Add(variantPWSM.Peptide.Protein, new HashSet { variant }); + frameshiftVariants.Add(variantPWSM.Protein, new HashSet { variant }); } } else if (culture.CompareInfo.IndexOf(variant.Description.Description, "stop_gained", CompareOptions.IgnoreCase) >= 0) @@ -1623,13 +1622,13 @@ private void WriteVariantResults() stopGainCount++; stopGainIdentified = true; } - if (stopGainVariants.ContainsKey(variantPWSM.Peptide.Protein)) + if (stopGainVariants.ContainsKey(variantPWSM.Protein)) { - stopGainVariants[variantPWSM.Peptide.Protein].Add(variant); + stopGainVariants[variantPWSM.Protein].Add(variant); } else { - stopGainVariants.Add(variantPWSM.Peptide.Protein, new HashSet { variant }); + stopGainVariants.Add(variantPWSM.Protein, new HashSet { variant }); } } else if ((culture.CompareInfo.IndexOf(variant.Description.Description, "conservative_inframe_insertion", CompareOptions.IgnoreCase) >= 0) || (culture.CompareInfo.IndexOf(variant.Description.Description, "disruptive_inframe_insertion", CompareOptions.IgnoreCase) >= 0)) @@ -1639,13 +1638,13 @@ private void WriteVariantResults() insertionCount++; insertionIdentified = true; } - if (insertionVariants.ContainsKey(variantPWSM.Peptide.Protein)) + if (insertionVariants.ContainsKey(variantPWSM.Protein)) { - insertionVariants[variantPWSM.Peptide.Protein].Add(variant); + insertionVariants[variantPWSM.Protein].Add(variant); } else { - insertionVariants.Add(variantPWSM.Peptide.Protein, new HashSet { variant }); + insertionVariants.Add(variantPWSM.Protein, new HashSet { variant }); } } else if ((culture.CompareInfo.IndexOf(variant.Description.Description, "conservative_inframe_deletion", CompareOptions.IgnoreCase) >= 0) || (culture.CompareInfo.IndexOf(variant.Description.Description, "disruptive_inframe_deletion", CompareOptions.IgnoreCase) >= 0)) @@ -1655,13 +1654,13 @@ private void WriteVariantResults() deletionCount++; deletionIdentified = true; } - if (deletionVariants.ContainsKey(variantPWSM.Peptide.Protein)) + if (deletionVariants.ContainsKey(variantPWSM.Protein)) { - deletionVariants[variantPWSM.Peptide.Protein].Add(variant); + deletionVariants[variantPWSM.Protein].Add(variant); } else { - deletionVariants.Add(variantPWSM.Peptide.Protein, new HashSet { variant }); + deletionVariants.Add(variantPWSM.Protein, new HashSet { variant }); } } else if (culture.CompareInfo.IndexOf(variant.Description.Description, "stop_loss", CompareOptions.IgnoreCase) >= 0) @@ -1671,13 +1670,13 @@ private void WriteVariantResults() stopLossCount++; stopLossIdentifed = true; } - if (stopLossVariants.ContainsKey(variantPWSM.Peptide.Protein)) + if (stopLossVariants.ContainsKey(variantPWSM.Protein)) { - stopLossVariants[variantPWSM.Peptide.Protein].Add(variant); + stopLossVariants[variantPWSM.Protein].Add(variant); } else { - stopLossVariants.Add(variantPWSM.Peptide.Protein, new HashSet { variant }); + stopLossVariants.Add(variantPWSM.Protein, new HashSet { variant }); } } } @@ -1843,11 +1842,11 @@ private static void WritePsmsForPercolator(List psmList, string w foreach (var peptide in psm.BestMatchingBioPolymersWithSetMods) { output.Write(idNumber.ToString()); - output.Write('\t' + (peptide.Peptide.Parent.IsDecoy ? -1 : 1).ToString()); + output.Write('\t' + (peptide.WithSetMods.Parent.IsDecoy ? -1 : 1).ToString()); output.Write('\t' + psm.ScanNumber.ToString()); output.Write(psm.PsmData_forPEPandPercolator.ToString(searchType)); - output.Write('\t' + (peptide.Peptide.PreviousResidue + "." + peptide.Peptide.FullSequence + "." + peptide.Peptide.NextResidue).ToString()); - output.Write('\t' + (peptide.Peptide.Parent.Accession).ToString()); + output.Write('\t' + (peptide.WithSetMods.PreviousResidue + "." + peptide.WithSetMods.FullSequence + "." + peptide.WithSetMods.NextResidue).ToString()); + output.Write('\t' + (peptide.WithSetMods.Parent.Accession).ToString()); output.WriteLine(); } idNumber++; diff --git a/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs b/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs index 619680eff8..4fccf3709d 100644 --- a/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs +++ b/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs @@ -549,18 +549,13 @@ public static void MatchInternalFragmentIons(SpectralMatch[] fileSpecificPsms, M scanForThisPsm.TheScan.DissociationType.Value : combinedParams.DissociationType; //Get the theoretical peptides - List ambiguousPeptides = new List(); List notches = new List(); - foreach (var (Notch, Peptide) in psm.BestMatchingBioPolymersWithSetMods) - { - ambiguousPeptides.Add(Peptide); - notches.Add(Notch); - } + var ambiguousPeptides = psm.BestMatchingBioPolymersWithSetMods.ToList(); //get matched ions for each peptide List> matchedIonsForAllAmbiguousPeptides = new List>(); List internalFragments = new List(); - foreach (PeptideWithSetModifications peptide in ambiguousPeptides) + foreach (IBioPolymerWithSetMods peptide in ambiguousPeptides.Select(p => p.WithSetMods)) { internalFragments.Clear(); peptide.FragmentInternally(dissociationType, minInternalFragmentLength, internalFragments); @@ -576,20 +571,22 @@ public static void MatchInternalFragmentIons(SpectralMatch[] fileSpecificPsms, M HashSet PeptidesToMatchingInternalFragments = new HashSet(); for (int peptideIndex = 0; peptideIndex < ambiguousPeptides.Count; peptideIndex++) { + var thisPeptide = ambiguousPeptides[peptideIndex]; //if we should remove the theoretical, remove it if (matchedIonsForAllAmbiguousPeptides[peptideIndex].Count + 1 < maxNumMatchedIons) { - psm.RemoveThisAmbiguousPeptide(notches[peptideIndex], ambiguousPeptides[peptideIndex]); + psm.RemoveThisAmbiguousPeptide(thisPeptide); } // otherwise add the matched internal ions to the total ions else { - IBioPolymerWithSetMods currentPwsm = ambiguousPeptides[peptideIndex]; + IBioPolymerWithSetMods currentPwsm = thisPeptide.WithSetMods; //check that we haven't already added the matched ions for this peptide if (!PeptidesToMatchingInternalFragments.Contains(currentPwsm)) { PeptidesToMatchingInternalFragments.Add(currentPwsm); //record that we've seen this peptide - psm.BioPolymersWithSetModsToMatchingFragments[currentPwsm].AddRange(matchedIonsForAllAmbiguousPeptides[peptideIndex]); //add the matched ions + + thisPeptide.MatchedIons.AddRange(matchedIonsForAllAmbiguousPeptides[peptideIndex]); //add the matched ions } } } diff --git a/MetaMorpheus/TaskLayer/XLSearchTask/WriteXlFile.cs b/MetaMorpheus/TaskLayer/XLSearchTask/WriteXlFile.cs index 65800667ab..f2688f1cfe 100644 --- a/MetaMorpheus/TaskLayer/XLSearchTask/WriteXlFile.cs +++ b/MetaMorpheus/TaskLayer/XLSearchTask/WriteXlFile.cs @@ -78,9 +78,9 @@ public static void WriteCrosslinkToTxtForPercolator(List + "\t" + item.BaseSequence.Length.ToString(CultureInfo.InvariantCulture) + "\t" + (item.BetaPeptide.BaseSequence.Length + item.BaseSequence.Length).ToString(CultureInfo.InvariantCulture) + "\t" + "-." + item.FullSequence + item.LinkPositions.First().ToString(CultureInfo.InvariantCulture) + "--" + item.BetaPeptide.FullSequence + item.BetaPeptide.LinkPositions.First().ToString(CultureInfo.InvariantCulture) + ".-" - + "\t" + item.BestMatchingBioPolymersWithSetMods.First().Peptide.Parent.Accession.ToString(CultureInfo.InvariantCulture) + + "\t" + item.BestMatchingBioPolymersWithSetMods.First().WithSetMods.Parent.Accession.ToString(CultureInfo.InvariantCulture) + "(" + (item.XlProteinPos.HasValue ? item.XlProteinPos.Value.ToString(CultureInfo.InvariantCulture) : string.Empty) + ")" - + "\t" + item.BetaPeptide.BestMatchingBioPolymersWithSetMods.First().Peptide.Parent.Accession.ToString(CultureInfo.InvariantCulture) + + "\t" + item.BetaPeptide.BestMatchingBioPolymersWithSetMods.First().WithSetMods.Parent.Accession.ToString(CultureInfo.InvariantCulture) + "(" + (item.BetaPeptide.XlProteinPos.HasValue ? item.BetaPeptide.XlProteinPos.Value.ToString(CultureInfo.InvariantCulture) : string.Empty) + ")" ); } @@ -199,7 +199,7 @@ public static void WritePepXML_xl(List items, List(); - var alphaPeptide = items[i].BestMatchingBioPolymersWithSetMods.First().Peptide; + var alphaPeptide = items[i].BestMatchingBioPolymersWithSetMods.First().WithSetMods; foreach (var modification in alphaPeptide.AllModsOneIsNterminus) { @@ -277,7 +277,7 @@ public static void WritePepXML_xl(List items, List(); foreach (var mod in betaPeptide.AllModsOneIsNterminus) diff --git a/MetaMorpheus/TaskLayer/XLSearchTask/XLSearchTask.cs b/MetaMorpheus/TaskLayer/XLSearchTask/XLSearchTask.cs index 9cc93daa5f..d57e21e000 100644 --- a/MetaMorpheus/TaskLayer/XLSearchTask/XLSearchTask.cs +++ b/MetaMorpheus/TaskLayer/XLSearchTask/XLSearchTask.cs @@ -331,11 +331,11 @@ public static List RemoveDuplicateFromCsmsPerScan(List sequences = new List(); foreach (SpectralMatch psm in nonNullPsms) { - var ss = psm.BestMatchingBioPolymersWithSetMods.Select(b => b.Peptide.FullSequence).ToList(); + var ss = psm.BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.FullSequence).ToList(); sequences.Add(String.Join("|", ss)); } @@ -205,7 +205,7 @@ public static void TestComputePEPValue() fileSpecificRetTimeHI_behavior.Add("TaGe_SA_HeLa_04_subset_longestSeq.mzML", HI_Time_avg_dev); int chargeStateMode = 4; - var (notch, pwsm) = maxScorePsm.BestMatchingBioPolymersWithSetMods.First(); + var bestMatch = maxScorePsm.BestMatchingBioPolymersWithSetMods.First(); Dictionary massError = new Dictionary { { Path.GetFileName(maxScorePsm.FullFilePath), 0 } @@ -235,16 +235,16 @@ public static void TestComputePEPValue() } } - var maxPsmData = pepEngine.CreateOnePsmDataEntry("standard", maxScorePsm, pwsm, notch, !pwsm.Parent.IsDecoy); - Assert.That(maxScorePsm.BioPolymersWithSetModsToMatchingFragments.Count - 1, Is.EqualTo(maxPsmData.Ambiguity)); - double normalizationFactor = (double)pwsm.BaseSequence.Length; + var maxPsmData = pepEngine.CreateOnePsmDataEntry("standard", maxScorePsm, bestMatch, !bestMatch.IsDecoy); + Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Count() - 1, Is.EqualTo(maxPsmData.Ambiguity)); + double normalizationFactor = (double)bestMatch.WithSetMods.BaseSequence.Length; float maxPsmDeltaScore = (float)Math.Round(maxScorePsm.DeltaScore / normalizationFactor * 10.0, 0); Assert.That(maxPsmDeltaScore, Is.EqualTo(maxPsmData.DeltaScore).Within(0.05)); float maxPsmIntensity = Math.Min(50, (float)Math.Round((maxScorePsm.Score - (int)maxScorePsm.Score) / normalizationFactor * 100.0, 0)); Assert.That(maxPsmIntensity, Is.EqualTo(maxPsmData.Intensity).Within(0.05)); Assert.That(maxPsmData.HydrophobicityZScore, Is.EqualTo(52.0).Within(0.05)); - Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide).First().MissedCleavages, Is.EqualTo(maxPsmData.MissedCleavagesCount)); - Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide).First().AllModsOneIsNterminus.Values.Count(), Is.EqualTo(maxPsmData.ModsCount)); + Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).First().MissedCleavages, Is.EqualTo(maxPsmData.MissedCleavagesCount)); + Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).First().AllModsOneIsNterminus.Values.Count(), Is.EqualTo(maxPsmData.ModsCount)); Assert.That(maxScorePsm.Notch ?? 0, Is.EqualTo(maxPsmData.Notch)); Assert.That(-Math.Abs(chargeStateMode - maxScorePsm.ScanPrecursorCharge), Is.EqualTo(maxPsmData.PrecursorChargeDiffToMode)); Assert.That((float)0, Is.EqualTo(maxPsmData.IsVariantPeptide)); @@ -298,7 +298,7 @@ public static void TestComputePEPValue() nonNullPsms.Add(variantPSM); foreach (SpectralMatch psm in nonNullPsms) { - var ss = psm.BestMatchingBioPolymersWithSetMods.Select(b => b.Peptide.FullSequence).ToList(); + var ss = psm.BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.FullSequence).ToList(); sequences.Add(String.Join("|", ss)); } @@ -308,7 +308,7 @@ public static void TestComputePEPValue() { sequenceToPsmCount.Add(grp.Key, grp.Count()); } - var (vnotch, vpwsm) = variantPSM.BestMatchingBioPolymersWithSetMods.First(); + var variantMatch = variantPSM.BestMatchingBioPolymersWithSetMods.First(); massError.Add(Path.GetFileName(variantPSM.FullFilePath), 0); @@ -327,7 +327,7 @@ public static void TestComputePEPValue() } - PsmData variantPsmData = pepEngine.CreateOnePsmDataEntry("standard", variantPSM, vpwsm, vnotch, !maxScorePsm.IsDecoy); + PsmData variantPsmData = pepEngine.CreateOnePsmDataEntry("standard", variantPSM, variantMatch, !maxScorePsm.IsDecoy); Assert.That(variantPsmData.IsVariantPeptide, Is.EqualTo((float)1)); @@ -428,7 +428,7 @@ public static void TestComputePEPValueTopDown() List sequences = new List(); foreach (SpectralMatch psm in nonNullPsms) { - var ss = psm.BestMatchingBioPolymersWithSetMods.Select(b => b.Peptide.FullSequence).ToList(); + var ss = psm.BestMatchingBioPolymersWithSetMods.Select(b => b.WithSetMods.FullSequence).ToList(); sequences.Add(String.Join(" | ", ss)); } var s = sequences.GroupBy(i => i); @@ -442,7 +442,7 @@ public static void TestComputePEPValueTopDown() Dictionary>> fileSpecificRetTemHI_behaviorModifiedPeptides = new Dictionary>>(); int chargeStateMode = 4; - var (notch, pwsm) = maxScorePsm.BestMatchingBioPolymersWithSetMods.First(); + var bestMatch = maxScorePsm.BestMatchingBioPolymersWithSetMods.First(); Dictionary massError = new Dictionary { @@ -467,16 +467,16 @@ public static void TestComputePEPValueTopDown() } } - var maxPsmData = pepEngine.CreateOnePsmDataEntry("top-down", maxScorePsm, pwsm, notch, !pwsm.Parent.IsDecoy); - Assert.That(maxScorePsm.BioPolymersWithSetModsToMatchingFragments.Count - 1, Is.EqualTo(maxPsmData.Ambiguity)); + var maxPsmData = pepEngine.CreateOnePsmDataEntry("top-down", maxScorePsm, bestMatch, !bestMatch.WithSetMods.Parent.IsDecoy); + Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Count() - 1, Is.EqualTo(maxPsmData.Ambiguity)); double normalizationFactor = 1; float maxPsmDeltaScore = (float)Math.Round(maxScorePsm.DeltaScore / normalizationFactor * 10.0, 0); Assert.That(maxPsmDeltaScore, Is.EqualTo(maxPsmData.DeltaScore).Within(0.05)); float maxPsmIntensity = (float)Math.Min(50, Math.Round((maxScorePsm.Score - (int)maxScorePsm.Score) / normalizationFactor * 100.0, 0)); Assert.That(maxPsmIntensity, Is.EqualTo(maxPsmData.Intensity).Within(0.05)); Assert.That(maxPsmData.HydrophobicityZScore, Is.EqualTo(float.NaN)); - Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide).First().MissedCleavages, Is.EqualTo(maxPsmData.MissedCleavagesCount)); - Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide).First().AllModsOneIsNterminus.Values.Count(), Is.EqualTo(maxPsmData.ModsCount)); + Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).First().MissedCleavages, Is.EqualTo(maxPsmData.MissedCleavagesCount)); + Assert.That(maxScorePsm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods).First().AllModsOneIsNterminus.Values.Count(), Is.EqualTo(maxPsmData.ModsCount)); Assert.That(maxScorePsm.Notch ?? 0, Is.EqualTo(maxPsmData.Notch)); Assert.That(-Math.Abs(chargeStateMode - maxScorePsm.ScanPrecursorCharge), Is.EqualTo(maxPsmData.PrecursorChargeDiffToMode)); Assert.That((float)0, Is.EqualTo(maxPsmData.IsVariantPeptide)); @@ -510,15 +510,7 @@ public static void TestPEP_peptideRemoval() Assert.That(indiciesOfPeptidesToRemove.FirstOrDefault(), Is.EqualTo(2)); Assert.That(pepValuePredictions.Count, Is.EqualTo(2)); - List notches = new List(); - List peptides = new List(); - foreach (var bmp in psm.BestMatchingBioPolymersWithSetMods) - { - notches.Add(bmp.Notch); - peptides.Add(bmp.Peptide); - } - - PepAnalysisEngine.RemoveBestMatchingPeptidesWithLowPEP(psm, indiciesOfPeptidesToRemove, notches, peptides, pepValuePredictions, ref ambiguousPeptidesRemovedCount); + PepAnalysisEngine.RemoveBestMatchingPeptidesWithLowPEP(psm, indiciesOfPeptidesToRemove, psm.BestMatchingBioPolymersWithSetMods.ToList(), ref ambiguousPeptidesRemovedCount); Assert.That(ambiguousPeptidesRemovedCount, Is.EqualTo(1)); Assert.That(psm.BestMatchingBioPolymersWithSetMods.Select(b => b.Notch).ToList().Count, Is.EqualTo(2)); } @@ -679,7 +671,7 @@ public static void TestRemoveThisAmbiguousePeptide() Assert.That(psm1.BestMatchingBioPolymersWithSetMods.Count(), Is.EqualTo(2)); - psm1.RemoveThisAmbiguousPeptide(1, pwsm); + psm1.RemoveThisAmbiguousPeptide(psm1.BestMatchingBioPolymersWithSetMods.ElementAt(1)); Assert.That(psm1.BestMatchingBioPolymersWithSetMods.Count(), Is.EqualTo(1)); } diff --git a/MetaMorpheus/Test/MatchIonsOfAllCharges.cs b/MetaMorpheus/Test/MatchIonsOfAllCharges.cs index 7388b3d026..64e7333f22 100644 --- a/MetaMorpheus/Test/MatchIonsOfAllCharges.cs +++ b/MetaMorpheus/Test/MatchIonsOfAllCharges.cs @@ -76,7 +76,7 @@ public static void TestMatchIonsOfAllChargesBottomUp() var peptideTheorProducts = new List(); Assert.That(psm_oneCharge[1].MatchedFragmentIons.Count == 12); var differences = psm[1].MatchedFragmentIons.Except(psm_oneCharge[1].MatchedFragmentIons); - psm[1].BestMatchingBioPolymersWithSetMods.First().Peptide.Fragment(CommonParameters.DissociationType, CommonParameters.DigestionParams.FragmentationTerminus, peptideTheorProducts); + psm[1].BestMatchingBioPolymersWithSetMods.First().WithSetMods.Fragment(CommonParameters.DissociationType, CommonParameters.DigestionParams.FragmentationTerminus, peptideTheorProducts); foreach (var ion in differences) { foreach (var product in peptideTheorProducts) @@ -174,7 +174,7 @@ public static void TestMatchIonsOfAllChargesTopDown() //compare 2 results and evaluate the different matched ions var peptideTheorProducts = new List(); var differences = psm.MatchedFragmentIons.Except(psm_oneCharge.MatchedFragmentIons); - psm.BestMatchingBioPolymersWithSetMods.First().Peptide.Fragment(CommonParameters.DissociationType, CommonParameters.DigestionParams.FragmentationTerminus, peptideTheorProducts); + psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods.Fragment(CommonParameters.DissociationType, CommonParameters.DigestionParams.FragmentationTerminus, peptideTheorProducts); foreach (var ion in differences) { foreach (var product in peptideTheorProducts) diff --git a/MetaMorpheus/Test/PsmTsvWriterTests.cs b/MetaMorpheus/Test/PsmTsvWriterTests.cs index d82f9a50fa..4c90ad520e 100644 --- a/MetaMorpheus/Test/PsmTsvWriterTests.cs +++ b/MetaMorpheus/Test/PsmTsvWriterTests.cs @@ -9,6 +9,7 @@ using Omics.Digestion; using Omics.Modifications; using Easy.Common.Extensions; +using EngineLayer.SpectrumMatch; namespace Test { @@ -82,7 +83,8 @@ public static void ResolveModificationsTest() Assert.That(matchedIonSeries, Is.EqualTo("[(b1-5.00)+1]")); //removing one of the peptides to reset for the next test - myPsm.RemoveThisAmbiguousPeptide(0, pwsm2); + var tentativeSpectralMatch = new TentativeSpectralMatch(0, pwsm2, mfi); + myPsm.RemoveThisAmbiguousPeptide(tentativeSpectralMatch); PeptideWithSetModifications pwsm3 = new PeptideWithSetModifications(protein1, new DigestionParams(), 2, 9, CleavageSpecificity.Unknown, null, 0, allModsOneIsNterminus1, 0); myPsm.AddOrReplace(pwsm3, 10, 0, true, mfi, 10); diff --git a/MetaMorpheus/Test/RobTest.cs b/MetaMorpheus/Test/RobTest.cs index afbfa0b0bd..e43b676ca2 100644 --- a/MetaMorpheus/Test/RobTest.cs +++ b/MetaMorpheus/Test/RobTest.cs @@ -124,7 +124,7 @@ public static void TestParsimony() proteinGroups = proteinScoringAndFdrResults.SortedAndScoredProteinGroups; // select the PSMs' proteins - List parsimonyProteinSequences = psms.SelectMany(p => p.BestMatchingBioPolymersWithSetMods.Select(v => v.Peptide.Parent)).Select(v => v.BaseSequence).Distinct().ToList(); + List parsimonyProteinSequences = psms.SelectMany(p => p.BestMatchingBioPolymersWithSetMods.Select(v => v.WithSetMods.Parent)).Select(v => v.BaseSequence).Distinct().ToList(); // check that correct proteins are in parsimony list Assert.That(parsimonyProteinSequences.Contains("AB--------")); diff --git a/MetaMorpheus/Test/SpectralRecoveryTest.cs b/MetaMorpheus/Test/SpectralRecoveryTest.cs index ef80f135e0..e0a97c54b2 100644 --- a/MetaMorpheus/Test/SpectralRecoveryTest.cs +++ b/MetaMorpheus/Test/SpectralRecoveryTest.cs @@ -259,7 +259,7 @@ public static void MiniClassicSearchEngineTest() foreach (SpectralMatch psm in allPsmsArray.Where(p => p != null)) { - IBioPolymerWithSetMods pwsm = psm.BestMatchingBioPolymersWithSetMods.First().Peptide; + IBioPolymerWithSetMods pwsm = psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods; MiniClassicSearchEngine mcse = new MiniClassicSearchEngine( listOfSortedms2Scans.OrderBy(p => p.RetentionTime).ToArray(), diff --git a/MetaMorpheus/Test/TestPsm.cs b/MetaMorpheus/Test/TestPsm.cs index 0f8fdb211f..1949a55dc9 100644 --- a/MetaMorpheus/Test/TestPsm.cs +++ b/MetaMorpheus/Test/TestPsm.cs @@ -102,7 +102,7 @@ public static void TestResolvedAmbiguitiesWithDiagnosticIon() // Check that the ion series with the diagnostic ion is set to the MatchedFragmentIons property Assert.That(psm.MatchedFragmentIons, Is.EqualTo(twoIonList)); // Check that the pwsm with the diagnostic ion is first in the BestMatchingBioPolymersWithSetMods list - Assert.That(psm.BestMatchingBioPolymersWithSetMods.First().Peptide, Is.EqualTo(pepWithOxidationOnP)); + Assert.That(psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods, Is.EqualTo(pepWithOxidationOnP)); } [Test] @@ -170,10 +170,10 @@ public static void TestPpmAndDaMassErrors() foreach (PeptideSpectralMatch psm in nonNullPsms) { double daError = - Math.Round(psm.ScanPrecursorMass - psm.BestMatchingBioPolymersWithSetMods.First().Peptide.MonoisotopicMass, 5); + Math.Round(psm.ScanPrecursorMass - psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods.MonoisotopicMass, 5); Assert.That(psm.PrecursorMassErrorDa.First(), Is.EqualTo(daError).Within(0.01)); - double ppmError = Math.Round((psm.ScanPrecursorMass - psm.BestMatchingBioPolymersWithSetMods.First().Peptide.MonoisotopicMass) / psm.BestMatchingBioPolymersWithSetMods.First().Peptide.MonoisotopicMass * 1e6, 5); + double ppmError = Math.Round((psm.ScanPrecursorMass - psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods.MonoisotopicMass) / psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods.MonoisotopicMass * 1e6, 5); Assert.That(psm.PrecursorMassErrorPpm.First(), Is.EqualTo(ppmError).Within(0.1)); } } @@ -205,9 +205,9 @@ public static void TestLongestFragmentIonSequence() { if (psm != null) { - foreach (var (Notch, Peptide) in psm.BestMatchingBioPolymersWithSetMods) + foreach (var bestMatch in psm.BestMatchingBioPolymersWithSetMods) { - longestSeriesObserved.Add(SpectralMatch.GetLongestIonSeriesBidirectional(psm.BioPolymersWithSetModsToMatchingFragments, Peptide)); + longestSeriesObserved.Add(SpectralMatch.GetLongestIonSeriesBidirectional(bestMatch.MatchedIons, bestMatch.WithSetMods)); } } } @@ -307,12 +307,12 @@ public static void TestPsmMatchingToTargetAndDecoyWithSameSequence() psm.AddOrReplace(decoy, 1, 0, true, null, 0); Assert.That(psm.BestMatchingBioPolymersWithSetMods.Count(), Is.EqualTo(2)); - Assert.That(psm.BestMatchingBioPolymersWithSetMods.Any(p => p.Peptide.Parent.IsDecoy)); + Assert.That(psm.BestMatchingBioPolymersWithSetMods.Any(p => p.WithSetMods.Parent.IsDecoy)); psm.ResolveAllAmbiguities(); Assert.That(psm.BestMatchingBioPolymersWithSetMods.Count(), Is.EqualTo(1)); - Assert.That(psm.BestMatchingBioPolymersWithSetMods.All(p => !p.Peptide.Parent.IsDecoy)); + Assert.That(psm.BestMatchingBioPolymersWithSetMods.All(p => !p.WithSetMods.Parent.IsDecoy)); Assert.That(!psm.IsDecoy); } @@ -333,7 +333,7 @@ public static void TestPsmMatchingToTargetAndDecoyWithDifferentSequences() psm.AddOrReplace(decoy, 1, 0, true, null, 0); Assert.That(psm.BestMatchingBioPolymersWithSetMods.Count(), Is.EqualTo(2)); - Assert.That(psm.BestMatchingBioPolymersWithSetMods.Any(p => p.Peptide.Parent.IsDecoy)); + Assert.That(psm.BestMatchingBioPolymersWithSetMods.Any(p => p.WithSetMods.Parent.IsDecoy)); psm.ResolveAllAmbiguities(); @@ -517,7 +517,7 @@ public static void TestComplementaryIons() PeptideWithSetModifications pwsm = new PeptideWithSetModifications(new Protein("PEPTIDE", "ACCESSION", "ORGANISM"), new DigestionParams(), 1, 2, CleavageSpecificity.Full, "", 0, new Dictionary(), 0); - int count = SpectralMatch.GetCountComplementaryIons(psm1.BioPolymersWithSetModsToMatchingFragments, pwsm); + int count = SpectralMatch.GetCountComplementaryIons([], pwsm); //No Matched Fragment Ions Returns 0 Assert.That(count, Is.EqualTo(0)); @@ -536,10 +536,7 @@ public static void TestComplementaryIons() mfiList.Add(new MatchedFragmentIon(prod, 1, 1, 1)); } - Dictionary> PTMF = new Dictionary>(); - PTMF.Add(pwsm, mfiList); - - count = SpectralMatch.GetCountComplementaryIons(PTMF, pwsm); + count = SpectralMatch.GetCountComplementaryIons(mfiList, pwsm); //BioPolymersWithSetModsToMatchingFragments Contains one N and one C ion so intersection Returns 1 Assert.That(count, Is.EqualTo(1)); } @@ -562,10 +559,8 @@ public static void Test_PSM_GetLongestIonSeries_NullChecks() Assert.That(longestSeries, Is.EqualTo(1)); //matchedFragments == null returns 1 - Dictionary> PeptidesToMatchingFragments = new Dictionary>(); - PeptidesToMatchingFragments.Add(pwsm, null); - longestSeries = SpectralMatch.GetLongestIonSeriesBidirectional(PeptidesToMatchingFragments, pwsm); + longestSeries = SpectralMatch.GetLongestIonSeriesBidirectional(null, pwsm); Assert.That(longestSeries, Is.EqualTo(1)); } diff --git a/MetaMorpheus/Test/BioPolymerNotchFragmentIonComparerTest.cs b/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs similarity index 75% rename from MetaMorpheus/Test/BioPolymerNotchFragmentIonComparerTest.cs rename to MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs index e38f232380..562c3b8df2 100644 --- a/MetaMorpheus/Test/BioPolymerNotchFragmentIonComparerTest.cs +++ b/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs @@ -1,21 +1,20 @@ -using Easy.Common.Extensions; -using EngineLayer; -using NUnit.Framework; -using Omics; +using NUnit.Framework; using Omics.Fragmentation; using Proteomics; using Proteomics.ProteolyticDigestion; using System.Collections.Generic; -using System.Linq; using System.Reflection; using Omics.Modifications; +using System.Diagnostics.CodeAnalysis; +using EngineLayer.SpectrumMatch; -namespace Test +namespace Test.UtilitiesTest { [TestFixture] + [ExcludeFromCodeCoverage] public static class BioPolymerNotchFragmentIonComparerTest { - private static BioPolymerNotchFragmentIonComparer<(int, IBioPolymerWithSetMods, List)> comparer; + private static BioPolymerNotchFragmentIonComparer comparer; private static Protein exampleProtein; private static PeptideWithSetModifications examplePwsm; private static MatchedFragmentIon exampleIon; @@ -25,7 +24,7 @@ public static class BioPolymerNotchFragmentIonComparerTest [SetUp] public static void Setup() { - comparer = new BioPolymerNotchFragmentIonComparer<(int, IBioPolymerWithSetMods, List)>(); + comparer = new BioPolymerNotchFragmentIonComparer(); exampleProtein = new Protein("PEPTIDEK", "accession"); examplePwsm = new PeptideWithSetModifications("PEPTIDEK", null, p: exampleProtein); exampleIon = new MatchedFragmentIon(new Product(ProductType.b, FragmentationTerminus.N, 1, 1, 1, 0), 100, 100, 1); @@ -38,7 +37,7 @@ public static void Compare_DifferentNotches() { var x = (0, _examplePwsm: examplePwsm, new List()); var y = (2, _examplePwsm: examplePwsm, new List()); - Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); } [Test] @@ -46,14 +45,14 @@ public static void Compare_DifferentFragmentIonCounts() { var x = (1, _examplePwsm: examplePwsm, new List { exampleIon }); var y = (1, _examplePwsm: examplePwsm, new List()); - Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); } [Test] public static void Compare_DifferentNumberOfMods() { var modifiedPwsm = new PeptideWithSetModifications("PEPTIDEK", null, p: exampleProtein); - fullSequenceProperty.SetValue(modifiedPwsm, "P[Oxidation]EPT[Reduction]IDEK", null); + fullSequenceProperty.SetValue(modifiedPwsm, "P[Oxidation]EPT[Reduction]IDEK", null); modDictField.SetValue(modifiedPwsm, new Dictionary { @@ -62,11 +61,11 @@ public static void Compare_DifferentNumberOfMods() }); var x = (0, _examplePwsm: examplePwsm, new List()); var y = (0, modifiedPwsm, new List()); - Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); // double check that mods are considered before sequence fullSequenceProperty.SetValue(modifiedPwsm, "AAAAAAAA", null); - Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); } [Test] @@ -80,7 +79,7 @@ public static void Compare_DifferentFullSequences() // Full sequences are compared alphabetically, and '[' comes before 'E' var x = (0, modifiedPwsmFirst, new List()); var y = (0, modifiedPwsmSecond, new List()); - Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); } [Test] @@ -90,7 +89,7 @@ public static void Compare_DifferentAccessions() var protein2 = new Protein("PEPTIDEK", "accession2"); var x = (1, new PeptideWithSetModifications("PEPTIDEK", null, p: protein1), new List()); var y = (1, new PeptideWithSetModifications("PEPTIDEK", null, p: protein2), new List()); - Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); } [Test] @@ -98,11 +97,24 @@ public static void Compare_DifferentStartResidues() { var x = (1, new PeptideWithSetModifications("PEPTIDEK", null, p: exampleProtein, oneBasedStartResidueInProtein: 1), new List()); var y = (1, new PeptideWithSetModifications("PEPTIDEK", null, p: exampleProtein, oneBasedStartResidueInProtein: 5), new List()); - Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); } - } - + [Test] + public static void Compare_NullPwsm() + { + var x = (0, (PeptideWithSetModifications)null, new List()); + var y = (0, examplePwsm, new List()); + Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); + x = (0, examplePwsm, new List()); + y = (0, null, new List()); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); + + x = (0, null, new List()); + y = (0, null, new List()); + Assert.That(comparer.Compare(x, y), Is.EqualTo(0)); + } + } } diff --git a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs new file mode 100644 index 0000000000..fa3707a325 --- /dev/null +++ b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs @@ -0,0 +1,462 @@ +using System; +using EngineLayer.Util; +using NUnit.Framework; +using Omics.Fragmentation; +using Omics; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using EngineLayer.SpectrumMatch; + +namespace Test.UtilitiesTest; + +[TestFixture] +[ExcludeFromCodeCoverage] +public class PriorityQueueWithSimpleTypesTests +{ + [Test] + public void Enqueue_ShouldAddItem() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + Assert.That(pq.Count, Is.EqualTo(1)); + } + + [Test] + public void Enqueue_ShouldRemoveLowestPriorityItem_WhenAtMaxCapacity() + { + var pq = new PriorityQueue(2); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + pq.Enqueue(3, "item3"); + Assert.That(pq.Count, Is.EqualTo(2)); + Assert.That(pq.Peek(), Is.EqualTo("item3")); + } + + [Test] + public void Dequeue_ShouldReturnHighestPriorityItem() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + var item = pq.Dequeue(); + Assert.That(item, Is.EqualTo("item2")); + Assert.That(pq.Count, Is.EqualTo(1)); + } + + [Test] + public void DequeueWithPriority_ShouldReturnHighestPriorityItem() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + var item = pq.DequeueWithPriority(); + Assert.That(item, Is.EqualTo((2, "item2"))); + Assert.That(pq.Count, Is.EqualTo(1)); + } + + [Test] + public void Peek_ShouldReturnHighestPriorityItemWithoutRemovingIt() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + var item = pq.Peek(); + Assert.That(item, Is.EqualTo("item2")); + Assert.That(pq.Count, Is.EqualTo(2)); + } + + [Test] + public void GetEnumerator_ShouldEnumerateItemsInPriorityOrder() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + pq.Enqueue(3, "item3"); + pq.Enqueue(2, "item2"); + var items = new List(pq); + Assert.That(items, Is.EqualTo(new List { "item3", "item2", "item1" })); + } + + [Test] + public void Dequeue_ShouldReturnNull_WhenQueueIsEmpty() + { + var pq = new PriorityQueue(2); + Assert.That(pq.Dequeue(), Is.Null); + Assert.That(pq.DequeueWithPriority(), Is.Null); + } + + [Test] + public void Peek_ShouldReturnNull_WhenQueueIsEmpty() + { + var pq = new PriorityQueue(2); + Assert.That(pq.Peek(), Is.Null); + } + + [Test] + public void Enqueue_ShouldHandleDuplicatePriorities() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + pq.Enqueue(1, "item2"); + pq.Enqueue(1, "item3"); + Assert.That(pq.Count, Is.EqualTo(3)); + var items = new List(pq); + Assert.That(items, Is.EqualTo(new List { "item1", "item2", "item3" })); + } + + [Test] + public void Enumerator_ShouldEnumerateItemsInPriorityOrder_WithDuplicatePriorities() + { + var pq = new PriorityQueue(3); + pq.Enqueue(2, "item2"); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item3"); + var items = new List(pq); + Assert.That(items, Is.EqualTo(new List { "item2", "item3", "item1" })); + } + + [Test] + public void Constructor_ShouldSetMaxCapacity() + { + var pq = new PriorityQueue(5); + Assert.That(pq.Count, Is.EqualTo(0)); + + // ensure the queue is at max capacity + for (int i = 0; i < 10; i++) + { + pq.Enqueue(i, $"item{i}"); + } + Assert.That(pq.Count, Is.EqualTo(5)); + + // ensure the intended items are in the queue + var items = new List(pq); + Assert.That(items, Is.EqualTo(new List { "item9", "item8", "item7", "item6", "item5" })); + } + + [Test] + public void Constructor_ShouldUseDefaultComparer() + { + var pq = new PriorityQueue(5); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + Assert.That(pq.Dequeue(), Is.EqualTo("item2")); + } + + [Test] + public void Constructor_ShouldUseCustomComparer() + { + var customComparer = Comparer.Create( + (x, y) => String.Compare(y, x, StringComparison.Ordinal)); + var pq = new PriorityQueue(5, customComparer); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + Assert.That(pq.Dequeue(), Is.EqualTo("item2")); + } + + [Test] + public void Enqueue_ShouldHandleNullValues() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, null); + pq.Enqueue(1, "null"); + pq.Enqueue(1, null); + Assert.That(pq.Count, Is.EqualTo(2)); + Assert.That(pq.Peek(), Is.EqualTo(null)); + } + + [Test] + public void Enqueue_ShouldHandleExtremePriorityValues() + { + var pq = new PriorityQueue(3); + pq.Enqueue(double.MaxValue, "max"); + pq.Enqueue(double.MinValue, "min"); + Assert.That(pq.Count, Is.EqualTo(2)); + Assert.That(pq.Peek(), Is.EqualTo("max")); + } + + [Test] + public void Enqueue_ShouldHandleNegativePriorities() + { + var pq = new PriorityQueue(3); + pq.Enqueue(-1, "negative"); + pq.Enqueue(-4, "more negative"); + Assert.That(pq.Count, Is.EqualTo(2)); + Assert.That(pq.Peek(), Is.EqualTo("negative")); + } + + [Test] + public void Enqueue_ShouldNotHandleDuplicateItems() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item"); + pq.Enqueue(1, "item"); + Assert.That(pq.Count, Is.EqualTo(1)); + } + + [Test] + public void Enqueue_ShouldHandleCustomComparerEdgeCases() + { + // here our comparer does not compare Item2. + // This means things with the same priority will appear to be the same thing and not get added a second time + + var customComparer = Comparer.Create( + (x, y) => 0); + var pq = new PriorityQueue(3, customComparer); + pq.Enqueue(1, "item1"); + pq.Enqueue(1, "item2"); + Assert.That(pq.Count, Is.EqualTo(1)); + Assert.That(pq.Peek(), Is.EqualTo("item1")); + } + + [Test] + public void Remove_ShouldRemoveItem() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + pq.Remove("item1"); + Assert.That(pq.Count, Is.EqualTo(1)); + Assert.That(pq.Peek(), Is.EqualTo("item2")); + } + + [Test] + public void Remove_ShouldNotRemoveNonExistentItem() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + pq.Remove("item3"); + Assert.That(pq.Count, Is.EqualTo(2)); + } + + [Test] + public void Remove_ShouldHandleNullItem() + { + var pq = new PriorityQueue(3); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + pq.Remove(null); + Assert.That(pq.Count, Is.EqualTo(2)); + } + + [Test] + public void Remove_ShouldHandleEmptyQueue() + { + var pq = new PriorityQueue(3); + pq.Remove("item1"); + Assert.That(pq.Count, Is.EqualTo(0)); + } + + [Test] + public void Remove_ShouldRemoveCorrectItem_WithCustomComparer() + { + var customComparer = Comparer.Create((x, y) => String.Compare(y, x, StringComparison.Ordinal)); + var pq = new PriorityQueue(3, customComparer); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + pq.Remove("item2"); + Assert.That(pq.Count, Is.EqualTo(1)); + Assert.That(pq.Peek(), Is.EqualTo("item1")); + } + + [Test] + public void ToList_ShouldReturnListOfItems_SmallNumberOfValues() + { + var pq = new PriorityQueue(5); + pq.Enqueue(1, "item1"); + pq.Enqueue(3, "item3"); + pq.Enqueue(2, "item2"); + + var list = pq.ToList(); + + Assert.That(list, Is.EqualTo(new string[] { "item3", "item2", "item1" })); + } + + [Test] + public void ToList_ShouldReturnListOfItems_LargeNumberOfValues() + { + var pq = new PriorityQueue(1000); + for (int i = 1000; i > 0; i--) + { + pq.Enqueue(i, i); + } + + var list = pq.ToList(); + + Assert.That(list, Is.EqualTo(Enumerable.Range(1, 1000).Reverse().ToList())); + } + + [Test] + public void ToListWithPriority_ShouldReturnListOfItemsWithPriority_SmallNumberOfValues() + { + var pq = new PriorityQueue(5); + pq.Enqueue(1, "item1"); + pq.Enqueue(3, "item3"); + pq.Enqueue(2, "item2"); + + var list = pq.ToListWithPriority(); + + Assert.That(list, Is.EqualTo(new List<(double, string)> { (3, "item3"), (2, "item2"), (1, "item1") })); + } + + [Test] + public void ToListWithPriority_ShouldReturnListOfItemsWithPriority_LargeNumberOfValues() + { + var pq = new PriorityQueue(1000); + for (int i = 1000; i > 0; i--) + { + pq.Enqueue(i, i); + } + + var list = pq.ToListWithPriority(); + + Assert.That(list, Is.EqualTo(Enumerable.Range(1, 1000).Reverse().Select(i => ((double)i, i)).ToList())); + } + + [Test] + public void ToArray_ShouldReturnArrayOfItems_SmallNumberOfValues() + { + var pq = new PriorityQueue(5); + pq.Enqueue(1, "item1"); + pq.Enqueue(3, "item3"); + pq.Enqueue(2, "item2"); + + var array = pq.ToArray(); + + Assert.That(array, Is.EqualTo(new string[] { "item3", "item2", "item1" })); + } + + [Test] + public void ToArray_ShouldReturnArrayOfItems_LargeNumberOfValues() + { + var pq = new PriorityQueue(1000); + for (int i = 1000; i > 0; i--) + { + pq.Enqueue(i, i); + } + + var array = pq.ToArray(); + + Assert.That(array, Is.EqualTo(Enumerable.Range(1, 1000).Reverse().ToArray())); + } + + [Test] + public void ToArrayWithPriority_ShouldReturnArrayOfItemsWithPriority_SmallNumberOfValues() + { + var pq = new PriorityQueue(5); + pq.Enqueue(1, "item1"); + pq.Enqueue(3, "item3"); + pq.Enqueue(2, "item2"); + + var array = pq.ToArrayWithPriority(); + + Assert.That(array, Is.EqualTo(new (double, string)[] { (3, "item3"), (2, "item2"), (1, "item1") })); + } + + [Test] + public void ToArrayWithPriority_ShouldReturnArrayOfItemsWithPriority_LargeNumberOfValues() + { + var pq = new PriorityQueue(1000); + for (int i = 1000; i > 0; i--) + { + pq.Enqueue(i, i); + } + + var array = pq.ToArrayWithPriority(); + + Assert.That(array, Is.EqualTo(Enumerable.Range(1, 1000).Reverse().Select(i => ((double)i, i)).ToArray())); + } +} + +[TestFixture] +[ExcludeFromCodeCoverage] +public class PriorityQueueWithAnonymousTypesTest +{ + [Test] + public void TestEnqueueAndDequeue() + { + var comparer = new BioPolymerNotchFragmentIonComparer(); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + (comparer: comparer); + + var psm1 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + var psm2 = (Score: 5.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + + var dequeuedPsm = priorityQueue.Dequeue(); + + Assert.That(dequeuedPsm, Is.EqualTo(psm1.Item2)); + } + + [Test] + public void TestPeek() + { + var comparer = new BioPolymerNotchFragmentIonComparer(); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + (comparer: comparer); + + var psm1 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + var psm2 = (Score: 5.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + + var peekedPsm = priorityQueue.Peek(); + + Assert.That(peekedPsm, Is.EqualTo(psm1.Item2)); + } + + [Test] + public void TestEnqueueBeyondCapacity() + { + var comparer = new BioPolymerNotchFragmentIonComparer(); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + (maxCapacity: 1, comparer: comparer); + + var psm1 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + var psm2 = (Score: 5.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + + Assert.That(priorityQueue.Count, Is.EqualTo(1)); + var remainingPsm = priorityQueue.Dequeue(); + Assert.That(remainingPsm, Is.EqualTo(psm1.Item2)); + } + + [Test] + public void TestEnumerator() + { + var comparer = new BioPolymerNotchFragmentIonComparer(); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + (comparer: comparer); + + var psm1 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + var psm2 = (Score: 5.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + var psm3 = (Score: 7.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + priorityQueue.Enqueue(psm3.Score, psm3.Item2); + + var items = new List<(int notch, IBioPolymerWithSetMods pwsm, List ions)>(priorityQueue); + Assert.That(items, Is.EqualTo(new List<(int notch, IBioPolymerWithSetMods pwsm, List ions)> { psm2.Item2, psm3.Item2, psm1.Item2 })); + } + + [Test] + public void TestConstructorWithDefaultComparer() + { + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)>(5); + Assert.That(priorityQueue.Count, Is.EqualTo(0)); + } + + [Test] + public void TestConstructorWithCustomComparer() + { + var customComparer = Comparer<(int notch, IBioPolymerWithSetMods pwsm, List ions)>.Create((x, y) => y.Item1.CompareTo(x.Item1)); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)>(5, customComparer); + Assert.That(priorityQueue.Count, Is.EqualTo(0)); + } +} \ No newline at end of file diff --git a/MetaMorpheus/Test/UtilitiesTest/SpectralMatchQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/SpectralMatchQueueTests.cs new file mode 100644 index 0000000000..1894fbf765 --- /dev/null +++ b/MetaMorpheus/Test/UtilitiesTest/SpectralMatchQueueTests.cs @@ -0,0 +1,381 @@ +using EngineLayer.Util; +using NUnit.Framework; +using Omics.Fragmentation; +using Omics; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using EngineLayer; +using Proteomics.ProteolyticDigestion; +using EngineLayer.SpectrumMatch; + +namespace Test.UtilitiesTest +{ + [TestFixture] + [ExcludeFromCodeCoverage] + public class SpectralMatchQueueTests + { + public class SpectralMatchQueue : PriorityQueue<(int Notch, IBioPolymerWithSetMods, List MatchedIons)> + { + public SpectralMatchQueue(int maxCapacity = int.MaxValue, IComparer<(int Notch, IBioPolymerWithSetMods, List MatchedIons)>? comparer = null) + : base(maxCapacity, comparer ??= new BioPolymerNotchFragmentIonComparer()) + { + } + } + + [Test] + public void Enqueue_ShouldAddItem() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(1, (1, null, new List())); + Assert.That(pq.Count, Is.EqualTo(1)); + } + + [Test] + public void Enqueue_ShouldRemoveLowestPriorityItem_WhenAtMaxCapacity() + { + var pq = new SpectralMatchQueue(2); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (2, null, new List())); + pq.Enqueue(3, (3, null, new List())); + Assert.That(pq.Count, Is.EqualTo(2)); + Assert.That(pq.Dequeue(), Is.EqualTo((3, (IBioPolymerWithSetMods)null, new List()))); + Assert.That(pq.Dequeue(), Is.EqualTo((2, (IBioPolymerWithSetMods)null, new List()))); + Assert.That(pq.Count, Is.EqualTo(0)); + } + + [Test] + public void Dequeue_ShouldReturnHighestPriorityItem() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (2, null, new List())); + var item = pq.Dequeue(); + Assert.That(item, Is.EqualTo((2, (IBioPolymerWithSetMods)null, new List()))); + Assert.That(pq.Count, Is.EqualTo(1)); + } + + [Test] + public void DequeueWithPriority_ShouldReturnHighestPriorityItem() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (2, null, new List())); + var item = pq.DequeueWithPriority(); + Assert.That(item, Is.EqualTo((2, (2, (IBioPolymerWithSetMods)null, new List())))); + Assert.That(pq.Count, Is.EqualTo(1)); + } + + [Test] + public void Peek_ShouldReturnHighestPriorityItemWithoutRemovingIt() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (2, null, new List())); + var item = pq.Peek(); + Assert.That(item, Is.EqualTo((2, (IBioPolymerWithSetMods)null, new List()))); + Assert.That(pq.Count, Is.EqualTo(2)); + } + + [Test] + public void GetEnumerator_ShouldEnumerateItemsInPriorityOrder() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(3, (3, null, new List())); + pq.Enqueue(2, (2, null, new List())); + var items = new List<(int, IBioPolymerWithSetMods, List)>(pq); + Assert.That(items, + Is.EqualTo(new List<(int, IBioPolymerWithSetMods, List)> + { + (3, null, new List()), + (2, null, new List()), + (1, null, new List()) + })); + } + + [Test] + public void Dequeue_ShouldReturnDefault_WhenQueueIsEmpty() + { + var pq = new SpectralMatchQueue(); + Assert.That(pq.DequeueWithPriority(), Is.Default); + Assert.That(pq.Dequeue(), Is.Default); + } + + [Test] + public void Peek_ShouldReturnDefault_WhenQueueIsEmpty() + { + var pq = new SpectralMatchQueue(); + Assert.That(pq.Peek(), Is.Default); + } + + [Test] + public void Enqueue_ShouldHandleDuplicatePriorities() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(1, (2, null, new List())); + pq.Enqueue(1, (1, null, new List())); // identical to first, not added + Assert.That(pq.Count, Is.EqualTo(2)); + + var items = pq.ToArray(); + Assert.That(items, + Is.EqualTo(new (int, IBioPolymerWithSetMods, List)[] + { + (1, null, new List()), + (2, null, new List()) + })); + } + + [Test] + public void Enumerator_ShouldEnumerateItemsInPriorityOrder_WithDuplicatePriorities() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(2, (2, null, new List())); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (1, null, new List())); + var items = new List<(int, IBioPolymerWithSetMods, List)>(); + + while (pq.Count > 0) + { + var val = pq.Dequeue(); + items.Add(val); + } + + Assert.That(items, + Is.EqualTo(new List<(int, IBioPolymerWithSetMods, List)> + { + (1, null, new List()), + (2, null, new List()), + (1, null, new List()) + })); + } + + [Test] + public void DequeWithPriorities_ShouldEnumerateItemsInPriorityOrder_WithDuplicatePriorities() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(2, (2, null, new List())); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (1, null, new List())); + var items = new List<(double, (int, IBioPolymerWithSetMods, List))?>(); + + while (pq.Count > 0) + { + var val = pq.DequeueWithPriority(); + items.Add(val); + } + + Assert.That(items, + Is.EqualTo(new List<(double, (int, IBioPolymerWithSetMods, List))> + { + (2, (1, null, new List())), + (2, (2, null, new List())), + (1, (1, null, new List())) + })); + } + + [Test] + public void Constructor_ShouldSetMaxCapacity() + { + var pq = new SpectralMatchQueue(5); + Assert.That(pq.Count, Is.EqualTo(0)); + + // ensure the queue is at max capacity + for (int i = 0; i < 10; i++) + { + pq.Enqueue(i, (i, null, new List())); + } + Assert.That(pq.Count, Is.EqualTo(5)); + + // ensure the intended items are in the queue + var items = pq.ToList(); + Assert.That(items, + Is.EqualTo(new List<(int, IBioPolymerWithSetMods, List)> + { + (9, null, new List()), (8, null, new List()), + (7, null, new List()), (6, null, new List()), + (5, null, new List()) + })); + } + + [Test] + public void Constructor_ShouldUseDefaultComparer() + { + var pq = new SpectralMatchQueue(5); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (2, null, new List())); + Assert.That(pq.Dequeue(), Is.EqualTo((2, (IBioPolymerWithSetMods)null, new List()))); + } + + [Test] + public void Constructor_ShouldUseCustomComparer() + { + var customComparer = new BioPolymerNotchFragmentIonComparer(); + var pq = new SpectralMatchQueue(5, customComparer); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (2, null, new List())); + Assert.That(pq.Dequeue(), Is.EqualTo((2, (IBioPolymerWithSetMods)null, new List()))); + } + + [Test] + public void Enqueue_ShouldHandleNullValues() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(2, (1, null, new List())); + pq.Enqueue(2, (1, null, new List())); + Assert.That(pq.Count, Is.EqualTo(2)); + Assert.That(pq.Peek(), Is.EqualTo((1, (IBioPolymerWithSetMods)null, new List()))); + } + + [Test] + public void Enqueue_ShouldHandleExtremePriorityValues() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(double.MaxValue, (1, null, new List())); + pq.Enqueue(double.MinValue, (1, null, new List())); + Assert.That(pq.Count, Is.EqualTo(2)); + Assert.That(pq.Peek(), Is.EqualTo((1, (IBioPolymerWithSetMods)null, new List()))); + } + + [Test] + public void Enqueue_ShouldHandleNegativePriorities() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(-1, (1, null, new List())); + pq.Enqueue(-4, (1, null, new List())); + Assert.That(pq.Count, Is.EqualTo(2)); + Assert.That(pq.Peek(), Is.EqualTo((1, (IBioPolymerWithSetMods)null, new List()))); + } + + [Test] + public void Enqueue_ShouldNotHandleDuplicateItems() + { + var pq = new SpectralMatchQueue(); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(1, (1, null, new List())); + Assert.That(pq.Count, Is.EqualTo(1)); + } + + [Test] + public void Enqueue_ShouldHandleCustomComparerEdgeCases() + { + // here our comparer does not compare Item2. + // This means things with the same priority will appear to be the same thing and not get added a second time + + var customComparer = Comparer<(int notch, IBioPolymerWithSetMods bpwsm, List MatchedIons)> + .Create((x, y) => y.Item1.CompareTo(x.Item1)); + var pq = new SpectralMatchQueue(3, customComparer); + pq.Enqueue(1, (1, null, new List())); + pq.Enqueue(1, (1, null, new List())); + Assert.That(pq.Count, Is.EqualTo(1)); + Assert.That(pq.Peek(), Is.EqualTo((1, (IBioPolymerWithSetMods)null, new List()))); + } + + [Test] + public void TestEnqueueAndDequeue() + { + var comparer = new BioPolymerNotchFragmentIonComparer(); + var priorityQueue = new SpectralMatchQueue(comparer: comparer); + + var psm1 = (Score: 10.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + var psm2 = (Score: 5.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + + var dequeuedPsm = priorityQueue.Dequeue(); + + Assert.That(dequeuedPsm, Is.EqualTo(psm1.Item2)); + } + + [Test] + public void TestPeek() + { + var comparer = new BioPolymerNotchFragmentIonComparer(); + var priorityQueue = new SpectralMatchQueue(comparer: comparer); + + var psm1 = (Score: 10.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + var psm2 = (Score: 5.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + + var peekedPsm = priorityQueue.Peek(); + + Assert.That(peekedPsm, Is.EqualTo(psm1.Item2)); + } + + [Test] + public void TestEnqueueBeyondCapacity() + { + var comparer = new BioPolymerNotchFragmentIonComparer(); + var priorityQueue = new SpectralMatchQueue(maxCapacity: 1, comparer: comparer); + + var psm1 = (Score: 10.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + var psm2 = (Score: 5.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + + Assert.That(priorityQueue.Count, Is.EqualTo(1)); + var remainingPsm = priorityQueue.Dequeue(); + Assert.That(remainingPsm, Is.EqualTo(psm1.Item2)); + } + + [Test] + public void TestEnumerator() + { + var comparer = new BioPolymerNotchFragmentIonComparer(); + var priorityQueue = new SpectralMatchQueue(comparer: comparer); + + var psm1 = (Score: 10.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + var psm2 = (Score: 5.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + var psm3 = (Score: 7.0, (notch: 1, bpwsm: null as IBioPolymerWithSetMods, MatchedIons: new List())); + + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + priorityQueue.Enqueue(psm3.Score, psm3.Item2); + + var items = new List<(int notch, IBioPolymerWithSetMods bpwsm, List MatchedIons)>(priorityQueue); + Assert.That(items, Is.EqualTo(new List<(int notch, IBioPolymerWithSetMods bpwsm, List MatchedIons)> { psm2.Item2, psm3.Item2, psm1.Item2 })); + } + + [Test] + public void TestConstructorWithDefaultComparer() + { + var priorityQueue = new SpectralMatchQueue(5); + Assert.That(priorityQueue.Count, Is.EqualTo(0)); + } + + [Test] + public void TestConstructorWithCustomComparer() + { + var customComparer = Comparer<(int notch, IBioPolymerWithSetMods bpwsm, List MatchedIons)> + .Create((x, y) => y.Item1.CompareTo(x.Item1)); + var priorityQueue = new SpectralMatchQueue(5, customComparer); + Assert.That(priorityQueue.Count, Is.EqualTo(0)); + } + + [Test] + public void Remove_ShouldRemoveSpecifiedItem() + { + var pq = new SpectralMatchQueue(); + var pwsm1 = new PeptideWithSetModifications("PEPTIDE", GlobalVariables.AllModsKnownDictionary); + var pwsm2 = new PeptideWithSetModifications("PE[UniProt:4-carboxyglutamate on E]PTIDE", GlobalVariables.AllModsKnownDictionary); + var psm1 = (Score: 10.0, (notch: 1, pwsm1, ions: new List())); + var psm2 = (Score: 5.0, (notch: 1, pwsm2, ions: new List())); + var psm3 = (Score: 7.0, (notch: 2, pwsm2, ions: new List())); + + pq.Enqueue(psm1.Score, psm1.Item2); + pq.Enqueue(psm2.Score, psm2.Item2); + pq.Enqueue(psm3.Score, psm3.Item2); + pq.Remove((1, pwsm1, [])); + + Assert.That(pq.Count, Is.EqualTo(2)); + + var best = pq.Peek(); + Assert.That(best, Is.EqualTo(psm3.Item2)); + } + } +} diff --git a/MetaMorpheus/Test/UtilitiesTest/TentativeSpectralMatchTest.cs b/MetaMorpheus/Test/UtilitiesTest/TentativeSpectralMatchTest.cs new file mode 100644 index 0000000000..33355a9d0e --- /dev/null +++ b/MetaMorpheus/Test/UtilitiesTest/TentativeSpectralMatchTest.cs @@ -0,0 +1,77 @@ +using EngineLayer.SpectrumMatch; +using NUnit.Framework; +using Omics.Fragmentation; +using Omics; +using System; +using System.Collections.Generic; +using EngineLayer; +using Proteomics.ProteolyticDigestion; +using System.Diagnostics.CodeAnalysis; + +namespace Test.UtilitiesTest +{ + [TestFixture] + [ExcludeFromCodeCoverage] + internal class TentativeSpectralMatchTest + { + IBioPolymerWithSetMods testPeptide1 = new PeptideWithSetModifications("PEPTIDE", GlobalVariables.AllModsKnownDictionary); + IBioPolymerWithSetMods testPeptide2 = new PeptideWithSetModifications("PE[UniProt:4-carboxyglutamate on E]PTIDE", GlobalVariables.AllModsKnownDictionary); + + [Test] + public void TestEquals_SameObject() + { + var matchedIons = new List(); + var tsm = new TentativeSpectralMatch(1, testPeptide1, matchedIons); + + Assert.That(tsm.Equals(tsm), Is.True); + } + + [Test] + public void TestEquals_NullObject() + { + var matchedIons = new List(); + var tsm = new TentativeSpectralMatch(1, testPeptide1, matchedIons); + + Assert.That(tsm.Equals(null), Is.False); + } + + [Test] + public void TestEquals_DifferentType() + { + var matchedIons = new List(); + var tsm = new TentativeSpectralMatch(1, testPeptide1, matchedIons); + + Assert.That(tsm.Equals(new object()), Is.False); + } + + [Test] + public void TestEquals_DifferentValues() + { + var matchedIons1 = new List(); + var matchedIons2 = new List { new MatchedFragmentIon(default, 1, 1, 1) }; + var tsm1 = new TentativeSpectralMatch(1, testPeptide1, matchedIons1); + var tsm2 = new TentativeSpectralMatch(2, testPeptide2, matchedIons2); + + Assert.That(tsm1.Equals(tsm2), Is.False); + } + + [Test] + public void TestEquals_SameValues() + { + var matchedIons = new List(); + var tsm1 = new TentativeSpectralMatch(1, testPeptide1, matchedIons); + var tsm2 = new TentativeSpectralMatch(1, testPeptide1, matchedIons); + + Assert.That(tsm1.Equals(tsm2), Is.True); + } + + [Test] + public void TestGetHashCode() + { + var matchedIons = new List(); + var tsm = new TentativeSpectralMatch(1, testPeptide1, matchedIons); + + Assert.That(tsm.GetHashCode(), Is.EqualTo(HashCode.Combine(1, testPeptide1, matchedIons))); + } + } +} diff --git a/MetaMorpheus/Test/XLTest.cs b/MetaMorpheus/Test/XLTest.cs index e8152324ad..6f3dc4e432 100644 --- a/MetaMorpheus/Test/XLTest.cs +++ b/MetaMorpheus/Test/XLTest.cs @@ -371,7 +371,7 @@ public static void TestCsmSort() csmOne.AddOrReplace(pwsmV_D, 3, 0, true, new List(), 0); csmOne.ResolveAllAmbiguities(); Assert.That(csmOne.BestMatchingBioPolymersWithSetMods.Count() == 1); - Assert.That(!csmOne.BestMatchingBioPolymersWithSetMods.First().Peptide.Parent.IsDecoy); + Assert.That(!csmOne.BestMatchingBioPolymersWithSetMods.First().WithSetMods.Parent.IsDecoy); } [Test] @@ -704,7 +704,7 @@ public static void XlTest_MoreComprehensive() } } - var intraPsmData = pepEngine.CreateOnePsmDataEntry("crosslink", intraCsm, intraCsm.BestMatchingBioPolymersWithSetMods.First().Peptide, intraCsm.BestMatchingBioPolymersWithSetMods.First().Notch, !intraCsm.BestMatchingBioPolymersWithSetMods.First().Peptide.Parent.IsDecoy); + var intraPsmData = pepEngine.CreateOnePsmDataEntry("crosslink", intraCsm, intraCsm.BestMatchingBioPolymersWithSetMods.First(), !intraCsm.BestMatchingBioPolymersWithSetMods.First().WithSetMods.Parent.IsDecoy); Assert.That(intraPsmData.AbsoluteAverageFragmentMassErrorFromMedian, Is.EqualTo(1.0).Within(0.1)); Assert.That(intraPsmData.AlphaIntensity, Is.EqualTo(1).Within(0.1)); Assert.That(intraPsmData.Ambiguity, Is.EqualTo(0)); @@ -731,9 +731,8 @@ public static void XlTest_MoreComprehensive() var singleCsmPsmData = pepEngine.CreateOnePsmDataEntry("standard", singleCsm, - singleCsm.BestMatchingBioPolymersWithSetMods.FirstOrDefault().Peptide, - singleCsm.BestMatchingBioPolymersWithSetMods.FirstOrDefault().Notch, - !singleCsm.BestMatchingBioPolymersWithSetMods.FirstOrDefault().Peptide.Parent.IsDecoy); + singleCsm.BestMatchingBioPolymersWithSetMods.FirstOrDefault(), + !singleCsm.BestMatchingBioPolymersWithSetMods.FirstOrDefault().IsDecoy); Assert.That(singleCsmPsmData.AbsoluteAverageFragmentMassErrorFromMedian, Is.EqualTo(8).Within(0.1)); Assert.That(singleCsmPsmData.AlphaIntensity, Is.EqualTo(0)); Assert.That(singleCsmPsmData.Ambiguity, Is.EqualTo(0)); @@ -759,7 +758,7 @@ public static void XlTest_MoreComprehensive() CrosslinkSpectralMatch loopCsm = firstCsmsFromListsOfCsms.Where(c => c.CrossType == PsmCrossType.Loop).OrderBy(c => -c.Score).First(); - var loopCsmPsmData = pepEngine.CreateOnePsmDataEntry("standard", loopCsm, loopCsm.BestMatchingBioPolymersWithSetMods.First().Peptide, loopCsm.BestMatchingBioPolymersWithSetMods.First().Notch, !loopCsm.BestMatchingBioPolymersWithSetMods.First().Peptide.Parent.IsDecoy); Assert.That(loopCsmPsmData.AbsoluteAverageFragmentMassErrorFromMedian, Is.EqualTo(6).Within(0.1)); + var loopCsmPsmData = pepEngine.CreateOnePsmDataEntry("standard", loopCsm, loopCsm.BestMatchingBioPolymersWithSetMods.First(), !loopCsm.BestMatchingBioPolymersWithSetMods.First().WithSetMods.Parent.IsDecoy); Assert.That(loopCsmPsmData.AbsoluteAverageFragmentMassErrorFromMedian, Is.EqualTo(6).Within(0.1)); Assert.That(loopCsmPsmData.AlphaIntensity, Is.EqualTo(0)); Assert.That(loopCsmPsmData.Ambiguity, Is.EqualTo(0)); Assert.That(loopCsmPsmData.BetaIntensity, Is.EqualTo(0)); @@ -1300,9 +1299,9 @@ public static void XLSearchTastWriteFileTest() Crosslinker xlinker = Crosslinker.ParseCrosslinkerFromString("DSSO\tK\tK\tT\tCID|HCD\t158.0038\t54.01056\t85.982635\t176.0143\t175.0303\t279.0777"); double someSortOfMass = csmAlpha.ScanPrecursorMass - csmAlpha.BetaPeptide.BioPolymerWithSetModsMonoisotopicMass.Value - csmAlpha.BioPolymerWithSetModsMonoisotopicMass.Value - xlinker.TotalMass; string someLongString = "-." + csmAlpha.FullSequence + csmAlpha.LinkPositions.First().ToString(CultureInfo.InvariantCulture) + "--" + csmAlpha.BetaPeptide.FullSequence + csmAlpha.BetaPeptide.LinkPositions.First().ToString(CultureInfo.InvariantCulture) + ".-"; - string someOtherString = csmAlpha.BestMatchingBioPolymersWithSetMods.First().Peptide.Parent.Accession.ToString(CultureInfo.InvariantCulture) + string someOtherString = csmAlpha.BestMatchingBioPolymersWithSetMods.First().WithSetMods.Parent.Accession.ToString(CultureInfo.InvariantCulture) + "(" + (csmAlpha.XlProteinPos.HasValue ? csmAlpha.XlProteinPos.Value.ToString(CultureInfo.InvariantCulture) : string.Empty) + ")"; - string lastRandomString = csmAlpha.BetaPeptide.BestMatchingBioPolymersWithSetMods.First().Peptide.Parent.Accession.ToString(CultureInfo.InvariantCulture) + string lastRandomString = csmAlpha.BetaPeptide.BestMatchingBioPolymersWithSetMods.First().WithSetMods.Parent.Accession.ToString(CultureInfo.InvariantCulture) + "(" + (csmAlpha.BetaPeptide.XlProteinPos.HasValue ? csmAlpha.BetaPeptide.XlProteinPos.Value.ToString(CultureInfo.InvariantCulture) : string.Empty) + ")"; Assert.That(csmAlpha.ScanRetentionTime.ToString(CultureInfo.InvariantCulture), Is.EqualTo("NaN"));