From b5a9ed87db12e4449a85d4c1b0dfe52aa7c94d22 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 15:29:55 -0600 Subject: [PATCH 01/20] Created and tested tentative PSM comparer --- .../BioPolymerNotchFragmentIonComparer.cs | 20 +++--- .../EngineLayer/Util/TentativePsmComparer.cs | 21 ++++++ .../BioPolymerNotchFragmentIonComparerTest.cs | 19 +++--- .../TentativePsmComparerTests.cs | 66 +++++++++++++++++++ 4 files changed, 104 insertions(+), 22 deletions(-) rename MetaMorpheus/EngineLayer/{ => Util}/BioPolymerNotchFragmentIonComparer.cs (64%) create mode 100644 MetaMorpheus/EngineLayer/Util/TentativePsmComparer.cs rename MetaMorpheus/Test/{ => UtilitiesTest}/BioPolymerNotchFragmentIonComparerTest.cs (91%) create mode 100644 MetaMorpheus/Test/UtilitiesTest/TentativePsmComparerTests.cs diff --git a/MetaMorpheus/EngineLayer/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs similarity index 64% rename from MetaMorpheus/EngineLayer/BioPolymerNotchFragmentIonComparer.cs rename to MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs index 8c2b1c528..6f4a28068 100644 --- a/MetaMorpheus/EngineLayer/BioPolymerNotchFragmentIonComparer.cs +++ b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs @@ -1,14 +1,10 @@ using Omics; using Omics.Fragmentation; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace EngineLayer +namespace EngineLayer.Util { - public class BioPolymerNotchFragmentIonComparer : Comparer<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + 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. @@ -23,16 +19,16 @@ public override int Compare((int notch, IBioPolymerWithSetMods pwsm, List +/// Compares possible PSMs first by score, then by the . +/// +public class TentativePsmComparer : Comparer<(double Score, (int notch, IBioPolymerWithSetMods pwsm, List ions))> +{ + private static readonly BioPolymerNotchFragmentIonComparer BioPolymerNotchFragmentIonComparer = new(); + public override int Compare((double Score, (int notch, IBioPolymerWithSetMods pwsm, List ions)) x, + (double Score, (int notch, IBioPolymerWithSetMods pwsm, List ions)) y) + { + if (Math.Abs(x.Score - y.Score) > SpectralMatch.ToleranceForScoreDifferentiation) + return x.Score.CompareTo(y.Score); // Higher score is better + else return BioPolymerNotchFragmentIonComparer.Compare(x.Item2, y.Item2); + } +} \ No newline at end of file diff --git a/MetaMorpheus/Test/BioPolymerNotchFragmentIonComparerTest.cs b/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs similarity index 91% rename from MetaMorpheus/Test/BioPolymerNotchFragmentIonComparerTest.cs rename to MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs index e38f23238..87085a24c 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 EngineLayer.Util; using Omics.Modifications; +using System.Diagnostics.CodeAnalysis; -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); @@ -53,7 +52,7 @@ public static void Compare_DifferentFragmentIonCounts() 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 { @@ -102,7 +101,7 @@ public static void Compare_DifferentStartResidues() } } - + } diff --git a/MetaMorpheus/Test/UtilitiesTest/TentativePsmComparerTests.cs b/MetaMorpheus/Test/UtilitiesTest/TentativePsmComparerTests.cs new file mode 100644 index 000000000..68cc8bc3f --- /dev/null +++ b/MetaMorpheus/Test/UtilitiesTest/TentativePsmComparerTests.cs @@ -0,0 +1,66 @@ +using EngineLayer.Util; +using NUnit.Framework; +using Omics; +using Omics.Fragmentation; +using Proteomics.ProteolyticDigestion; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using EngineLayer; + +namespace Test.UtilitiesTest +{ + [TestFixture] + [ExcludeFromCodeCoverage] + public class TentativePsmComparerTests + { + [Test] + public void TestCompare_ScoreDifference() + { + var comparer = new TentativePsmComparer(); + 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())); + + int result = comparer.Compare(psm1, psm2); + + Assert.That(result, Is.EqualTo(1)); + } + + [Test] + public void TestCompare_SameScore_DifferentNotch() + { + var comparer = new TentativePsmComparer(); + var psm1 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + var psm2 = (Score: 10.0, (notch: 2, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + + int result = comparer.Compare(psm1, psm2); + + Assert.That(result, Is.EqualTo(1)); + } + + [Test] + public void TestCompare_SameScoreAndNotch_DifferentIonCount() + { + var comparer = new TentativePsmComparer(); + var psm1 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); + var psm2 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List() { default(MatchedFragmentIon)})); + + int result = comparer.Compare(psm1, psm2); + + Assert.That(result, Is.EqualTo(-1)); + } + + [Test] + public void TestCompare_SameScoreAndNotch_DifferentMod() + { + var comparer = new TentativePsmComparer(); + 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, pwsm: pwsm1, ions: new List())); + var psm2 = (Score: 10.0, (notch: 1, pwsm: pwsm2, ions: new List() { default(MatchedFragmentIon) })); + + int result = comparer.Compare(psm1, psm2); + + Assert.That(result, Is.EqualTo(-1)); + } + } +} From 9ed6dc8f5a12289664c88a932b74a680c52450bc Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 15:37:44 -0600 Subject: [PATCH 02/20] Created and tested priorityqueue --- .../EngineLayer/Util/PriorityQueue.cs | 74 ++++++++++ .../Test/UtilitiesTest/PriorityQueueTests.cs | 138 ++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 MetaMorpheus/EngineLayer/Util/PriorityQueue.cs create mode 100644 MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs diff --git a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs new file mode 100644 index 000000000..f99dfbe8a --- /dev/null +++ b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs @@ -0,0 +1,74 @@ +#nullable enable +using System; +using System.Collections; +using System.Collections.Generic; +namespace EngineLayer.Util; + +public class PriorityQueue : IEnumerable +{ + private readonly SortedSet<(double, T)> _sortedSet; + private readonly IComparer<(double, T)> _comparer; + private readonly int _maxCapacity; + + /// + /// 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<(double, T)>? comparer = null) + { + _comparer = comparer ?? Comparer<(double, T)>.Create((x, y) => + { + int priorityComparison = x.Item1.CompareTo(y.Item1); + if (priorityComparison != 0) + return priorityComparison; + 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; + if (x.Item2 is IComparable comparable) + return comparable.CompareTo(y.Item2); + return x.Item2.GetHashCode().CompareTo(y.Item2.GetHashCode()); + }); + _sortedSet = new SortedSet<(double, T)>(_comparer); + _maxCapacity = maxCapacity; + } + + 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.Min); + } + _sortedSet.Add((priority, item)); + } + + public T? Dequeue() + { + var max = _sortedSet.Max; + _sortedSet.Remove(max); + return max.Item2; + } + + public T? Peek() + { + return _sortedSet.Max.Item2; + } + + public int Count => _sortedSet.Count; + 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/Test/UtilitiesTest/PriorityQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs new file mode 100644 index 000000000..38b0fff34 --- /dev/null +++ b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs @@ -0,0 +1,138 @@ +using EngineLayer.Util; +using NUnit.Framework; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Test.UtilitiesTest +{ + [TestFixture] + [ExcludeFromCodeCoverage] + public class PriorityQueueTests + { + [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 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 { "item1", "item2", "item3" })); + } + + [Test] + public void Dequeue_IsNull_WhenQueueIsEmpty() + { + var pq = new PriorityQueue(2); + Assert.That(pq.Dequeue(), Is.Null); + } + + [Test] + public void Peek_IsNull_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 { "item1", "item2", "item3" })); + } + + [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 { "item5", "item6", "item7", "item8", "item9" })); + } + + [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<(double, string)>.Create((x, y) => y.Item1.CompareTo(x.Item1)); + var pq = new PriorityQueue(5, customComparer); + pq.Enqueue(1, "item1"); + pq.Enqueue(2, "item2"); + Assert.That(pq.Dequeue(), Is.EqualTo("item1")); + } + } +} From eefdc5baa4c1bcfd322f77c9ef8af8db059608b1 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 16:07:39 -0600 Subject: [PATCH 03/20] Handled null BPWSM in BpNotchFragmentIonComparer --- .../Util/BioPolymerNotchFragmentIonComparer.cs | 7 +++++++ .../BioPolymerNotchFragmentIonComparerTest.cs | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs index 6f4a28068..f378176e5 100644 --- a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs +++ b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs @@ -19,6 +19,13 @@ public override int Compare((int notch, IBioPolymerWithSetMods pwsm, List()); Assert.That(comparer.Compare(x, y), Is.GreaterThan(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)); + } + } } From 0f8a2f26609c23a62719dd7381d366a1c39bff42 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 16:17:44 -0600 Subject: [PATCH 04/20] Comment PrioroityQueue. Added Test cases for TentativePsmComparer --- .../EngineLayer/Util/PriorityQueue.cs | 37 ++++- .../Test/UtilitiesTest/PriorityQueueTests.cs | 149 +++++++++++++++++- 2 files changed, 179 insertions(+), 7 deletions(-) diff --git a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs index f99dfbe8a..0c4342007 100644 --- a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs +++ b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs @@ -4,12 +4,22 @@ using System.Collections.Generic; 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 SortedSet<(double, T)> _sortedSet; - private readonly IComparer<(double, T)> _comparer; private readonly int _maxCapacity; + /// + /// Gets the number of elements in the priority queue. + /// + public int Count => _sortedSet.Count; + /// /// Constructs a priority queue with a maximum capacity /// @@ -17,7 +27,7 @@ public class PriorityQueue : IEnumerable /// Default comparer compares priority then hash code of T public PriorityQueue(int maxCapacity = 128, IComparer<(double, T)>? comparer = null) { - _comparer = comparer ?? Comparer<(double, T)>.Create((x, y) => + IComparer<(double, T)> comparer1 = comparer ?? Comparer<(double, T)>.Create((x, y) => { int priorityComparison = x.Item1.CompareTo(y.Item1); if (priorityComparison != 0) @@ -32,10 +42,16 @@ public PriorityQueue(int maxCapacity = 128, IComparer<(double, T)>? comparer = n return comparable.CompareTo(y.Item2); return x.Item2.GetHashCode().CompareTo(y.Item2.GetHashCode()); }); - _sortedSet = new SortedSet<(double, T)>(_comparer); + _sortedSet = new SortedSet<(double, T)>(comparer1); _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) @@ -46,6 +62,10 @@ public void Enqueue(double priority, T item) _sortedSet.Add((priority, item)); } + /// + /// Removes and returns the element with the highest priority from the queue. + /// + /// The element with the highest priority, or null if the queue is empty. public T? Dequeue() { var max = _sortedSet.Max; @@ -53,12 +73,19 @@ public void Enqueue(double priority, T item) return max.Item2; } + /// + /// 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.Max.Item2; } - - public int Count => _sortedSet.Count; + + /// + /// 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) diff --git a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs index 38b0fff34..3a3e520d2 100644 --- a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs +++ b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs @@ -1,5 +1,7 @@ using EngineLayer.Util; using NUnit.Framework; +using Omics.Fragmentation; +using Omics; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -62,14 +64,14 @@ public void GetEnumerator_ShouldEnumerateItemsInPriorityOrder() } [Test] - public void Dequeue_IsNull_WhenQueueIsEmpty() + public void Dequeue_ShouldReturnNull_WhenQueueIsEmpty() { var pq = new PriorityQueue(2); Assert.That(pq.Dequeue(), Is.Null); } [Test] - public void Peek_IsNull_WhenQueueIsEmpty() + public void Peek_ShouldReturnNull_WhenQueueIsEmpty() { var pq = new PriorityQueue(2); Assert.That(pq.Peek(), Is.Null); @@ -134,5 +136,148 @@ public void Constructor_ShouldUseCustomComparer() pq.Enqueue(2, "item2"); Assert.That(pq.Dequeue(), Is.EqualTo("item1")); } + + [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<(double, string)> + .Create((x, y) => y.Item1.CompareTo(x.Item1)); + 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 TestEnqueueAndDequeue() + { + var comparer = new TentativePsmComparer(); + 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 TentativePsmComparer(); + 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 TentativePsmComparer(); + 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 TentativePsmComparer(); + 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<(double, (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)); + } } } From 346a1a97b68d56808c253622870c165f63384ebf Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 16:23:59 -0600 Subject: [PATCH 05/20] Added dequewithPriority to priorityQueue --- MetaMorpheus/EngineLayer/Util/PriorityQueue.cs | 15 +++++++++++++++ .../Test/UtilitiesTest/PriorityQueueTests.cs | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs index 0c4342007..7dcfbb772 100644 --- a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs +++ b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs @@ -73,6 +73,21 @@ public void Enqueue(double priority, T item) return max.Item2; } + /// + /// Removes and returns the element with the highest priority from the queue. + /// + /// The element with the highest priority and its priority, or null if the queue is empty. + public (double, T)? DequeueWithPriority() + { + var max = _sortedSet.Max; + // if queue is empty, max will be the default anonymous type (0, null) + if (EqualityComparer<(double, T)>.Default.Equals(max, default((double, T)))) + return null; + + _sortedSet.Remove(max); + return max; + } + /// /// Returns the element with the highest priority without removing it from the queue. /// diff --git a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs index 3a3e520d2..945d25d95 100644 --- a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs +++ b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs @@ -41,6 +41,17 @@ public void Dequeue_ShouldReturnHighestPriorityItem() 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() { @@ -68,6 +79,7 @@ public void Dequeue_ShouldReturnNull_WhenQueueIsEmpty() { var pq = new PriorityQueue(2); Assert.That(pq.Dequeue(), Is.Null); + Assert.That(pq.DequeueWithPriority(), Is.Null); } [Test] From d525d5776dec013080a779688860f2bcf529d4a7 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 18:00:57 -0600 Subject: [PATCH 06/20] Refactor PriorityQueue and enhance null safety Refactored PriorityQueue class for better encapsulation and added new methods for converting the queue to different collection types. Improved null safety in BioPolymerNotchFragmentIonComparer.cs by using the null-conditional operator. Reorganized and expanded test cases for PriorityQueue: - Renamed and reformatted PriorityQueueTests class. - Added new test methods for various scenarios. - Introduced a new test fixture for anonymous types. --- .../BioPolymerNotchFragmentIonComparer.cs | 4 +- .../EngineLayer/Util/PriorityQueue.cs | 60 +- .../Test/UtilitiesTest/PriorityQueueTests.cs | 591 +++++++++++------- 3 files changed, 392 insertions(+), 263 deletions(-) diff --git a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs index f378176e5..6b00ab243 100644 --- a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs +++ b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs @@ -32,8 +32,8 @@ public override int Compare((int notch, IBioPolymerWithSetMods pwsm, List @@ -12,37 +14,38 @@ namespace EngineLayer.Util; /// The type of elements in the priority queue. public class PriorityQueue : IEnumerable { - private readonly SortedSet<(double, T)> _sortedSet; + protected readonly SortedSet<(double, T)> SortedSet; + protected readonly IComparer<(double, T)> Comparer; private readonly int _maxCapacity; /// /// Gets the number of elements in the priority queue. /// - public int Count => _sortedSet.Count; + 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<(double, T)>? comparer = null) + public PriorityQueue(int maxCapacity = 128, IComparer<(double, T)>? comparer = null) { - IComparer<(double, T)> comparer1 = comparer ?? Comparer<(double, T)>.Create((x, y) => + Comparer = comparer ?? Comparer<(double, T)>.Create((x, y) => { int priorityComparison = x.Item1.CompareTo(y.Item1); if (priorityComparison != 0) - return priorityComparison; + return -priorityComparison; 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; + if (y.Item2 is null) + return -1; if (x.Item2 is IComparable comparable) return comparable.CompareTo(y.Item2); return x.Item2.GetHashCode().CompareTo(y.Item2.GetHashCode()); }); - _sortedSet = new SortedSet<(double, T)>(comparer1); + SortedSet = new SortedSet<(double, T)>(Comparer); _maxCapacity = maxCapacity; } @@ -54,38 +57,41 @@ public PriorityQueue(int maxCapacity = 128, IComparer<(double, T)>? comparer = n /// The element to be added to the queue. public void Enqueue(double priority, T item) { - if (_sortedSet.Count >= _maxCapacity) + if (SortedSet.Count >= _maxCapacity) { // Remove the item with the lowest priority (last item) if the queue is at max capacity - _sortedSet.Remove(_sortedSet.Min); + SortedSet.Remove(SortedSet.Max); } - _sortedSet.Add((priority, item)); + SortedSet.Add((priority, item)); } /// /// Removes and returns the element with the highest priority from the queue. /// - /// The element with the highest priority, or null if the queue is empty. + /// The element with the highest priority, or the default if queue is empty. public T? Dequeue() { - var max = _sortedSet.Max; - _sortedSet.Remove(max); - return max.Item2; + 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 null if the queue is empty. + /// The element with the highest priority and its priority, or default if the queue is empty. public (double, T)? DequeueWithPriority() { - var max = _sortedSet.Max; + var highestPriority = SortedSet.Min; // if queue is empty, max will be the default anonymous type (0, null) - if (EqualityComparer<(double, T)>.Default.Equals(max, default((double, T)))) - return null; + if (EqualityComparer<(double, T)>.Default.Equals(highestPriority, default((double, T)))) + return default; - _sortedSet.Remove(max); - return max; + SortedSet.Remove(highestPriority); + return highestPriority; } /// @@ -94,16 +100,24 @@ public void Enqueue(double priority, T item) /// The element with the highest priority, or null if the queue is empty. public T? Peek() { - return _sortedSet.Max.Item2; + return SortedSet.Min.Item2; } + 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) + foreach (var item in SortedSet) { yield return item.Item2; } diff --git a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs index 945d25d95..1c2ea18be 100644 --- a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs +++ b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs @@ -4,292 +4,407 @@ using Omics; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; -namespace Test.UtilitiesTest +namespace Test.UtilitiesTest; + +[TestFixture] +[ExcludeFromCodeCoverage] +public class PriorityQueueWithSimpleTypesTests { - [TestFixture] - [ExcludeFromCodeCoverage] - public class PriorityQueueTests + [Test] + public void Enqueue_ShouldAddItem() { - [Test] - public void Enqueue_ShouldAddItem() - { - var pq = new PriorityQueue(3); - pq.Enqueue(1, "item1"); - Assert.That(pq.Count, Is.EqualTo(1)); - } + 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 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 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 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 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 { "item1", "item2", "item3" })); - } + [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 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 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 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 { "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 { "item5", "item6", "item7", "item8", "item9" })); - } + [Test] + public void Constructor_ShouldSetMaxCapacity() + { + var pq = new PriorityQueue(5); + Assert.That(pq.Count, Is.EqualTo(0)); - [Test] - public void Constructor_ShouldUseDefaultComparer() + // ensure the queue is at max capacity + for (int i = 0; i < 10; i++) { - var pq = new PriorityQueue(5); - pq.Enqueue(1, "item1"); - pq.Enqueue(2, "item2"); - Assert.That(pq.Dequeue(), Is.EqualTo("item2")); + pq.Enqueue(i, $"item{i}"); } + Assert.That(pq.Count, Is.EqualTo(5)); - [Test] - public void Constructor_ShouldUseCustomComparer() - { - var customComparer = Comparer<(double, string)>.Create((x, y) => y.Item1.CompareTo(x.Item1)); - var pq = new PriorityQueue(5, customComparer); - pq.Enqueue(1, "item1"); - pq.Enqueue(2, "item2"); - Assert.That(pq.Dequeue(), Is.EqualTo("item1")); - } + // 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 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 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 Enqueue_ShouldHandleExtremePriorityValues() + [Test] + public void Constructor_ShouldUseCustomComparer() + { + var customComparer = Comparer<(double, string)>.Create( + (x, y) => y.Item1.CompareTo(x.Item1)); + 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<(double, string)> + .Create((x, y) => y.Item1.CompareTo(x.Item1)); + 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 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--) { - 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")); + pq.Enqueue(i, i); } - [Test] - public void Enqueue_ShouldHandleNegativePriorities() + 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--) { - 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")); + pq.Enqueue(i, i); } - [Test] - public void Enqueue_ShouldNotHandleDuplicateItems() + 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--) { - var pq = new PriorityQueue(3); - pq.Enqueue(1, "item"); - pq.Enqueue(1, "item"); - Assert.That(pq.Count, Is.EqualTo(1)); + pq.Enqueue(i, i); } - [Test] - public void Enqueue_ShouldHandleCustomComparerEdgeCases() + 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--) { - // 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<(double, string)> - .Create((x, y) => y.Item1.CompareTo(x.Item1)); - 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")); + pq.Enqueue(i, i); } - [Test] - public void TestEnqueueAndDequeue() - { - var comparer = new TentativePsmComparer(); - var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> - (comparer: comparer); + var array = pq.ToArrayWithPriority(); - 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())); + 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 TentativePsmComparer(); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + (comparer: comparer); - priorityQueue.Enqueue(psm1.Score, psm1.Item2); - priorityQueue.Enqueue(psm2.Score, psm2.Item2); + 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 dequeuedPsm = priorityQueue.Dequeue(); + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); - Assert.That(dequeuedPsm, Is.EqualTo(psm1.Item2)); - } + var dequeuedPsm = priorityQueue.Dequeue(); - [Test] - public void TestPeek() - { - var comparer = new TentativePsmComparer(); - var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> - (comparer: comparer); + Assert.That(dequeuedPsm, Is.EqualTo(psm1.Item2)); + } - 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())); + [Test] + public void TestPeek() + { + var comparer = new TentativePsmComparer(); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + (comparer: comparer); - priorityQueue.Enqueue(psm1.Score, psm1.Item2); - priorityQueue.Enqueue(psm2.Score, psm2.Item2); + 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 peekedPsm = priorityQueue.Peek(); + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); - Assert.That(peekedPsm, Is.EqualTo(psm1.Item2)); - } + var peekedPsm = priorityQueue.Peek(); - [Test] - public void TestEnqueueBeyondCapacity() - { - var comparer = new TentativePsmComparer(); - var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> - (maxCapacity: 1, comparer: comparer); + Assert.That(peekedPsm, Is.EqualTo(psm1.Item2)); + } - 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())); + [Test] + public void TestEnqueueBeyondCapacity() + { + var comparer = new TentativePsmComparer(); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + (maxCapacity: 1, comparer: comparer); - priorityQueue.Enqueue(psm1.Score, psm1.Item2); - priorityQueue.Enqueue(psm2.Score, psm2.Item2); + 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())); - Assert.That(priorityQueue.Count, Is.EqualTo(1)); - var remainingPsm = priorityQueue.Dequeue(); - Assert.That(remainingPsm, Is.EqualTo(psm1.Item2)); - } + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); - [Test] - public void TestEnumerator() - { - var comparer = new TentativePsmComparer(); - var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> - (comparer: comparer); + Assert.That(priorityQueue.Count, Is.EqualTo(1)); + var remainingPsm = priorityQueue.Dequeue(); + Assert.That(remainingPsm, Is.EqualTo(psm1.Item2)); + } - 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())); + [Test] + public void TestEnumerator() + { + var comparer = new TentativePsmComparer(); + var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + (comparer: comparer); - priorityQueue.Enqueue(psm1.Score, psm1.Item2); - priorityQueue.Enqueue(psm2.Score, psm2.Item2); - priorityQueue.Enqueue(psm3.Score, psm3.Item2); + 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())); - 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 })); - } + priorityQueue.Enqueue(psm1.Score, psm1.Item2); + priorityQueue.Enqueue(psm2.Score, psm2.Item2); + priorityQueue.Enqueue(psm3.Score, psm3.Item2); - [Test] - public void TestConstructorWithDefaultComparer() - { - var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)>(5); - Assert.That(priorityQueue.Count, Is.EqualTo(0)); - } + 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 TestConstructorWithCustomComparer() - { - var customComparer = Comparer<(double, (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)); - } + [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<(double, (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 From 094808ef8d67673ad5a3c8806ff49d1f818361b3 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 18:36:57 -0600 Subject: [PATCH 07/20] Priority queue uses an internal comparer for all values of T --- .../EngineLayer/Util/PriorityQueue.cs | 16 ++--- .../EngineLayer/Util/TentativePsmComparer.cs | 21 ------ .../Test/UtilitiesTest/PriorityQueueTests.cs | 21 +++--- .../TentativePsmComparerTests.cs | 66 ------------------- 4 files changed, 19 insertions(+), 105 deletions(-) delete mode 100644 MetaMorpheus/EngineLayer/Util/TentativePsmComparer.cs delete mode 100644 MetaMorpheus/Test/UtilitiesTest/TentativePsmComparerTests.cs diff --git a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs index 45f90b286..2c262b0c0 100644 --- a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs +++ b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs @@ -14,9 +14,10 @@ namespace EngineLayer.Util; /// 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; - private readonly int _maxCapacity; + protected readonly IComparer InternalComparer; /// /// Gets the number of elements in the priority queue. @@ -28,22 +29,21 @@ public class PriorityQueue : IEnumerable /// /// Maximum number of results to keep /// Default comparer compares priority then hash code of T - public PriorityQueue(int maxCapacity = 128, IComparer<(double, T)>? comparer = null) + public PriorityQueue(int maxCapacity = 128, IComparer? comparer = null) { - Comparer = comparer ?? Comparer<(double, T)>.Create((x, y) => + InternalComparer = comparer ?? Comparer.Default; + Comparer = Comparer<(double, T)>.Create((x, y) => { int priorityComparison = x.Item1.CompareTo(y.Item1); if (priorityComparison != 0) - return -priorityComparison; + 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; - if (x.Item2 is IComparable comparable) - return comparable.CompareTo(y.Item2); - return x.Item2.GetHashCode().CompareTo(y.Item2.GetHashCode()); + return 1; + return InternalComparer.Compare(x.Item2, y.Item2); }); SortedSet = new SortedSet<(double, T)>(Comparer); _maxCapacity = maxCapacity; diff --git a/MetaMorpheus/EngineLayer/Util/TentativePsmComparer.cs b/MetaMorpheus/EngineLayer/Util/TentativePsmComparer.cs deleted file mode 100644 index 04159b41c..000000000 --- a/MetaMorpheus/EngineLayer/Util/TentativePsmComparer.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using Omics; -using Omics.Fragmentation; - -namespace EngineLayer.Util; - -/// -/// Compares possible PSMs first by score, then by the . -/// -public class TentativePsmComparer : Comparer<(double Score, (int notch, IBioPolymerWithSetMods pwsm, List ions))> -{ - private static readonly BioPolymerNotchFragmentIonComparer BioPolymerNotchFragmentIonComparer = new(); - public override int Compare((double Score, (int notch, IBioPolymerWithSetMods pwsm, List ions)) x, - (double Score, (int notch, IBioPolymerWithSetMods pwsm, List ions)) y) - { - if (Math.Abs(x.Score - y.Score) > SpectralMatch.ToleranceForScoreDifferentiation) - return x.Score.CompareTo(y.Score); // Higher score is better - else return BioPolymerNotchFragmentIonComparer.Compare(x.Item2, y.Item2); - } -} \ No newline at end of file diff --git a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs index 1c2ea18be..1b999248d 100644 --- a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs +++ b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs @@ -1,3 +1,4 @@ +using System; using EngineLayer.Util; using NUnit.Framework; using Omics.Fragmentation; @@ -143,8 +144,8 @@ public void Constructor_ShouldUseDefaultComparer() [Test] public void Constructor_ShouldUseCustomComparer() { - var customComparer = Comparer<(double, string)>.Create( - (x, y) => y.Item1.CompareTo(x.Item1)); + 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"); @@ -159,7 +160,7 @@ public void Enqueue_ShouldHandleNullValues() pq.Enqueue(1, "null"); pq.Enqueue(1, null); Assert.That(pq.Count, Is.EqualTo(2)); - Assert.That(pq.Peek(), Is.EqualTo("null")); + Assert.That(pq.Peek(), Is.EqualTo(null)); } [Test] @@ -197,8 +198,8 @@ 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<(double, string)> - .Create((x, y) => y.Item1.CompareTo(x.Item1)); + var customComparer = Comparer.Create( + (x, y) => 0); var pq = new PriorityQueue(3, customComparer); pq.Enqueue(1, "item1"); pq.Enqueue(1, "item2"); @@ -323,7 +324,7 @@ public class PriorityQueueWithAnonymousTypesTest [Test] public void TestEnqueueAndDequeue() { - var comparer = new TentativePsmComparer(); + var comparer = new BioPolymerNotchFragmentIonComparer(); var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> (comparer: comparer); @@ -341,7 +342,7 @@ public void TestEnqueueAndDequeue() [Test] public void TestPeek() { - var comparer = new TentativePsmComparer(); + var comparer = new BioPolymerNotchFragmentIonComparer(); var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> (comparer: comparer); @@ -359,7 +360,7 @@ public void TestPeek() [Test] public void TestEnqueueBeyondCapacity() { - var comparer = new TentativePsmComparer(); + var comparer = new BioPolymerNotchFragmentIonComparer(); var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> (maxCapacity: 1, comparer: comparer); @@ -377,7 +378,7 @@ public void TestEnqueueBeyondCapacity() [Test] public void TestEnumerator() { - var comparer = new TentativePsmComparer(); + var comparer = new BioPolymerNotchFragmentIonComparer(); var priorityQueue = new PriorityQueue<(int notch, IBioPolymerWithSetMods pwsm, List ions)> (comparer: comparer); @@ -403,7 +404,7 @@ public void TestConstructorWithDefaultComparer() [Test] public void TestConstructorWithCustomComparer() { - var customComparer = Comparer<(double, (int notch, IBioPolymerWithSetMods pwsm, List ions))>.Create((x, y) => y.Item1.CompareTo(x.Item1)); + 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)); } diff --git a/MetaMorpheus/Test/UtilitiesTest/TentativePsmComparerTests.cs b/MetaMorpheus/Test/UtilitiesTest/TentativePsmComparerTests.cs deleted file mode 100644 index 68cc8bc3f..000000000 --- a/MetaMorpheus/Test/UtilitiesTest/TentativePsmComparerTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -using EngineLayer.Util; -using NUnit.Framework; -using Omics; -using Omics.Fragmentation; -using Proteomics.ProteolyticDigestion; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using EngineLayer; - -namespace Test.UtilitiesTest -{ - [TestFixture] - [ExcludeFromCodeCoverage] - public class TentativePsmComparerTests - { - [Test] - public void TestCompare_ScoreDifference() - { - var comparer = new TentativePsmComparer(); - 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())); - - int result = comparer.Compare(psm1, psm2); - - Assert.That(result, Is.EqualTo(1)); - } - - [Test] - public void TestCompare_SameScore_DifferentNotch() - { - var comparer = new TentativePsmComparer(); - var psm1 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); - var psm2 = (Score: 10.0, (notch: 2, pwsm: null as IBioPolymerWithSetMods, ions: new List())); - - int result = comparer.Compare(psm1, psm2); - - Assert.That(result, Is.EqualTo(1)); - } - - [Test] - public void TestCompare_SameScoreAndNotch_DifferentIonCount() - { - var comparer = new TentativePsmComparer(); - var psm1 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List())); - var psm2 = (Score: 10.0, (notch: 1, pwsm: null as IBioPolymerWithSetMods, ions: new List() { default(MatchedFragmentIon)})); - - int result = comparer.Compare(psm1, psm2); - - Assert.That(result, Is.EqualTo(-1)); - } - - [Test] - public void TestCompare_SameScoreAndNotch_DifferentMod() - { - var comparer = new TentativePsmComparer(); - 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, pwsm: pwsm1, ions: new List())); - var psm2 = (Score: 10.0, (notch: 1, pwsm: pwsm2, ions: new List() { default(MatchedFragmentIon) })); - - int result = comparer.Compare(psm1, psm2); - - Assert.That(result, Is.EqualTo(-1)); - } - } -} From c778a0f0ff9317d6bce08718c6d5c1b3ce709e20 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 18:44:16 -0600 Subject: [PATCH 08/20] Inverted BioPOlymerNotchFragmentIOnComparer --- MetaMorpheus/EngineLayer/SpectralMatch.cs | 5 ++- .../BioPolymerNotchFragmentIonComparer.cs | 38 +++++++++---------- .../BioPolymerNotchFragmentIonComparerTest.cs | 18 ++++----- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/MetaMorpheus/EngineLayer/SpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectralMatch.cs index 6ca0ed6f0..1fa0761a0 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -9,6 +9,7 @@ using Omics; using System; using EngineLayer.CrosslinkSearch; +using EngineLayer.Util; namespace EngineLayer { @@ -124,7 +125,7 @@ public List PrecursorMassErrorPpm #region Search public DigestionParams DigestionParams { get; } - public static BioPolymerNotchFragmentIonComparer<(int notch, IBioPolymerWithSetMods pwsm, List ions)> BioPolymerNotchFragmentIonComparer = new(); + public static BioPolymerNotchFragmentIonComparer BioPolymerNotchFragmentIonComparer = new(); // 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. @@ -140,7 +141,7 @@ public List PrecursorMassErrorPpm // 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 => + return _BestMatchingBioPolymersWithSetMods.OrderBy(t => (t.Notch, t.Pwsm, BioPolymersWithSetModsToMatchingFragments.TryGetValue(t.Pwsm, out var ions) ? ions : null), comparer: BioPolymerNotchFragmentIonComparer); } diff --git a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs index 6b00ab243..20abb160b 100644 --- a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs +++ b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs @@ -4,38 +4,38 @@ namespace EngineLayer.Util { - public class BioPolymerNotchFragmentIonComparer : Comparer<(int notch, IBioPolymerWithSetMods pwsm, List ions)> + public class BioPolymerNotchFragmentIonComparer : Comparer<(int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons)> { /// - /// 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. + /// 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 pwsm, List ions) x, (int notch, IBioPolymerWithSetMods pwsm, List ions) y) + public override int Compare((int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons) x, (int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons) y) { - if (x.notch != y.notch) - return -1 * x.notch.CompareTo(y.notch); // Lower notch is better + if (x.Notch != y.Notch) + return 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.MatchedIons?.Count != y.MatchedIons?.Count && !ReferenceEquals(x.MatchedIons, null)) + return -1 * x.MatchedIons.Count.CompareTo(y.MatchedIons?.Count); // More ions are better - if (x.pwsm == null && y.pwsm == null) + if (x.Bpwsm == null && y.Bpwsm == null) return 0; - if (x.pwsm == null) - return 1; // Null pwsm is considered worse - if (y.pwsm == null) - return -1; // Null pwsm is considered worse + if (x.Bpwsm == null) + return -1; // Null Bpwsm is considered worse + if (y.Bpwsm == null) + return 1; // Null Bpwsm is considered worse - if (x.pwsm.NumMods != y.pwsm.NumMods) - return -1 * x.pwsm.NumMods.CompareTo(y.pwsm.NumMods); // Fewer mods are better + if (x.Bpwsm.NumMods != y.Bpwsm.NumMods) + return x.Bpwsm.NumMods.CompareTo(y.Bpwsm.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.Bpwsm.FullSequence != y.Bpwsm.FullSequence) + return string.Compare(x.Bpwsm.FullSequence, y.Bpwsm.FullSequence); // 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 + 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 -1 * x.pwsm.OneBasedStartResidue.CompareTo(y.pwsm.OneBasedStartResidue); + return x.Bpwsm.OneBasedStartResidue.CompareTo(y.Bpwsm.OneBasedStartResidue); } } } diff --git a/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs b/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs index c8bbb5ebb..673b75800 100644 --- a/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs +++ b/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs @@ -37,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] @@ -45,7 +45,7 @@ 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] @@ -61,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] @@ -79,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] @@ -89,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] @@ -97,7 +97,7 @@ 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] @@ -105,11 +105,11 @@ 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)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); x = (0, examplePwsm, new List()); y = (0, null, new List()); - Assert.That(comparer.Compare(x, y), Is.LessThan(0)); + Assert.That(comparer.Compare(x, y), Is.GreaterThan(0)); x = (0, null, new List()); y = (0, null, new List()); From 9007b1dfbf55c0f8dc2d19da99457917f2e60189 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sat, 1 Mar 2025 23:30:43 -0600 Subject: [PATCH 09/20] Priority Queue, added remove --- .../EngineLayer/Util/PriorityQueue.cs | 16 +++++- .../Test/UtilitiesTest/PriorityQueueTests.cs | 50 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs index 2c262b0c0..c61cd9bab 100644 --- a/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs +++ b/MetaMorpheus/EngineLayer/Util/PriorityQueue.cs @@ -103,6 +103,21 @@ public void Enqueue(double priority, T item) 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(); @@ -122,7 +137,6 @@ public IEnumerator GetEnumerator() yield return item.Item2; } } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs index 1b999248d..2b352de46 100644 --- a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs +++ b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs @@ -207,6 +207,56 @@ public void Enqueue_ShouldHandleCustomComparerEdgeCases() 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() From 243a699fa07f95c1a2afa1d92bb82ad120b9ef75 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 00:37:07 -0600 Subject: [PATCH 10/20] TentativeSpectralMatch Creation and Compilation --- .../Calibration/DataPointAcquisitionEngine.cs | 4 +- .../ClassicSearch/MiniClassicSearchEngine.cs | 7 +- .../CrosslinkSearch/CrosslinkSpectralMatch.cs | 10 +- .../FdrAnalysis/FdrAnalysisEngine.cs | 10 +- .../FdrAnalysis/PEPAnalysisEngine.cs | 104 +++++++++--------- .../GlycoSearch/GlycoSearchEngine.cs | 6 +- .../GlycoSearch/GlycoSpectralMatch.cs | 12 +- MetaMorpheus/EngineLayer/Gptmd/GptmdEngine.cs | 2 +- .../Localization/LocalizationEngine.cs | 4 +- .../ModificationAnalysisEngine.cs | 4 +- .../NonSpecificEnzymeSearchEngine.cs | 4 +- .../EngineLayer/OligoSpectralMatch.cs | 3 +- .../EngineLayer/PeptideSpectralMatch.cs | 11 +- .../ProteinParsimony/ProteinGroup.cs | 6 +- .../ProteinParsimonyEngine.cs | 28 ++--- .../ProteinScoringAndFdrEngine.cs | 10 +- .../EngineLayer/PsmTsv/PsmTsvWriter.cs | 2 +- .../EngineLayer/Silac/SilacConversions.cs | 21 ++-- .../SpectralLibrarySearchFunction.cs | 12 +- MetaMorpheus/EngineLayer/SpectralMatch.cs | 93 +++++++--------- .../SpectrumMatch/TentativeSpectralMatch.cs | 15 +++ MetaMorpheus/EngineLayer/Util/AnalyteType.cs | 2 +- .../BioPolymerNotchFragmentIonComparer.cs | 4 +- .../PostGlycoSearchAnalysisTask.cs | 6 +- .../MbrAnalysis/SpectralRecoveryRunner.cs | 2 +- MetaMorpheus/TaskLayer/PepXMLWriter.cs | 4 +- .../TaskLayer/SearchTask/MzIdentMLWriter.cs | 10 +- .../SearchTask/PostSearchAnalysisTask.cs | 75 +++++++------ .../TaskLayer/SearchTask/SearchTask.cs | 14 +-- .../TaskLayer/XLSearchTask/WriteXlFile.cs | 8 +- .../TaskLayer/XLSearchTask/XLSearchTask.cs | 4 +- MetaMorpheus/Test/FdrTest.cs | 42 +++---- MetaMorpheus/Test/GPTMDengineTest.cs | 2 +- MetaMorpheus/Test/MatchIonsOfAllCharges.cs | 4 +- .../Test/MultiProteaseParsimonyTest.cs | 2 +- MetaMorpheus/Test/PsmTsvWriterTests.cs | 4 +- MetaMorpheus/Test/RobTest.cs | 2 +- MetaMorpheus/Test/SilacTest.cs | 2 +- MetaMorpheus/Test/SpectralRecoveryTest.cs | 2 +- MetaMorpheus/Test/TestOGlyco.cs | 2 +- MetaMorpheus/Test/TestPsm.cs | 16 +-- MetaMorpheus/Test/XLTest.cs | 15 ++- MetaMorpheus/Test/gptmdPrunedBdTests.cs | 4 +- 43 files changed, 291 insertions(+), 303 deletions(-) create mode 100644 MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs diff --git a/MetaMorpheus/EngineLayer/Calibration/DataPointAcquisitionEngine.cs b/MetaMorpheus/EngineLayer/Calibration/DataPointAcquisitionEngine.cs index 90fc0ad06..19a0c26be 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 80ad9eff7..885989c15 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 4bdf2522c..033ff0f3f 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 ac07d9174..d057391b7 100644 --- a/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs +++ b/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs @@ -150,7 +150,7 @@ private void DoFalseDiscoveryRateAnalysis(FdrAnalysisResults myAnalysisResults) /// /// This methods assumes that PSMs are already sorted appropriately for downstream usage /// Then, it counts the number of targets and (fractional) decoys, writes those values to the - /// appropriate FdrInfo (PSM or Peptide level), and calculates q-values + /// appropriate FdrInfo (PSM or WithSetMods level), and calculates q-values /// public void CalculateQValue(List psms, bool peptideLevelCalculation, bool pepCalculation = false) { @@ -167,7 +167,7 @@ public void CalculateQValue(List psms, bool peptideLevelCalculati // Stop if canceled if (GlobalVariables.StopLoops) { break; } - // we have to keep track of q-values separately for each notch + // we have to keep track of q-values separately for each Notch int notch = psm.Notch ?? MassDiffAcceptorNumNotches; if (psm.IsDecoy) { @@ -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++; } @@ -256,7 +256,7 @@ private void QValueInverted(List psms, bool peptideLevelAnalysis) if (GlobalVariables.StopLoops) { break; } int notch = psms[i].Notch ?? MassDiffAcceptorNumNotches; - // populate the highest q-Value for each notch + // populate the highest q-Value for each Notch if (!qValueNotchCalculated[notch]) { qValueNotch[notch] = (psms[0].GetFdrInfo(peptideLevelAnalysis).CumulativeDecoyNotch + 1) / psms[0].GetFdrInfo(peptideLevelAnalysis).CumulativeTargetNotch; diff --git a/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs b/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs index efbbe6b6b..8e1e2b215 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 (psm.BioPolymersWithSetModsToMatchingFragments[tentativeSpectralMatch.WithSetMods]?.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 = 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(psm.BioPolymersWithSetModsToMatchingFragments, tentativeSpectralMatch.WithSetMods) / normalizationFactor * multiplier, 0); + complementaryIonCount = (float)Math.Round(SpectralMatch.GetCountComplementaryIons(psm.BioPolymersWithSetModsToMatchingFragments, 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,8 +564,8 @@ 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(); + PeptideWithSetModifications selectedAlphaPeptide = csm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods as PeptideWithSetModifications).First(); + PeptideWithSetModifications selectedBetaPeptide = csm.BetaPeptide?.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods as PeptideWithSetModifications).First(); float alphaNormalizationFactor = selectedAlphaPeptide.BaseSequence.Length; float betaNormalizationFactor = selectedBetaPeptide == null ? (float)0 : selectedBetaPeptide.BaseSequence.Length; @@ -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)); diff --git a/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSearchEngine.cs b/MetaMorpheus/EngineLayer/GlycoSearch/GlycoSearchEngine.cs index af9ba0e58..6e9996df5 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 ec29d613c..1f662434a 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 f5482ad4d..8d5313e93 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 97ac5f525..650b978b5 100644 --- a/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs +++ b/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs @@ -12,7 +12,7 @@ namespace EngineLayer.Localization /// /// The mass difference between an experimental precursor and the theoretical mass of the assigned peptide is determined. The LocalizationEngine attempts /// to localize this mass to one of the residues. It does this by adding the mass difference to each theoretical ion mass and looking for additional matches - /// in the experimental spectrum. This engine should only be run for open, notch or custom searches. It should not be run for exact mass or missed + /// in the experimental spectrum. This engine should only be run for open, Notch or custom searches. It should not be run for exact mass or missed /// monoisopic searches. /// public class LocalizationEngine : MetaMorpheusEngine @@ -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 209196e10..d8ff78519 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/NonSpecificEnzymeSearch/NonSpecificEnzymeSearchEngine.cs b/MetaMorpheus/EngineLayer/NonSpecificEnzymeSearch/NonSpecificEnzymeSearchEngine.cs index ff18c3c27..9f33ecb05 100644 --- a/MetaMorpheus/EngineLayer/NonSpecificEnzymeSearch/NonSpecificEnzymeSearchEngine.cs +++ b/MetaMorpheus/EngineLayer/NonSpecificEnzymeSearch/NonSpecificEnzymeSearchEngine.cs @@ -335,7 +335,7 @@ private Tuple Accepts(List fragments, { foreach (Modification mod in terminalModsAtThisIndex) { - notch = searchMode.Accepts(scanPrecursorMass, theoMass + mod.MonoisotopicMass.Value); //overwrite the notch, since the other notch wasn't accepted + notch = searchMode.Accepts(scanPrecursorMass, theoMass + mod.MonoisotopicMass.Value); //overwrite the Notch, since the other Notch wasn't accepted if (notch >= 0) { terminalMod = mod; @@ -408,7 +408,7 @@ private Tuple Accepts(List fragments, { foreach (Modification terminalMod in terminalModsAtThisIndex) { - notch = searchMode.Accepts(scanPrecursorMass, totalMass + terminalMod.MonoisotopicMass.Value); //overwrite the notch, since the other notch wasn't accepted + notch = searchMode.Accepts(scanPrecursorMass, totalMass + terminalMod.MonoisotopicMass.Value); //overwrite the Notch, since the other Notch wasn't accepted if (notch >= 0) { //need to update the mod dictionary and don't want to overwrite the peptide incase it's in other scans diff --git a/MetaMorpheus/EngineLayer/OligoSpectralMatch.cs b/MetaMorpheus/EngineLayer/OligoSpectralMatch.cs index d32130288..21f959c23 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 1e23205e0..7dcf9a95c 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 e882391b0..23bf00e23 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 081aab800..ce4d9cdf5 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,9 @@ private List RunProteinParsimonyEngine() // create any new associations that need to be made foreach (PeptideSpectralMatch psm in baseSequence.Value) { - IBioPolymerWithSetMods originalPeptide = psm.BestMatchingBioPolymersWithSetMods.First().Peptide; + IBioPolymerWithSetMods originalPeptide = psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods; List mfi = psm.BioPolymersWithSetModsToMatchingFragments[originalPeptide]; - HashSet psmProteins = new HashSet(psm.BestMatchingBioPolymersWithSetMods.Select(p => p.Peptide.Parent as Protein)); + HashSet psmProteins = new HashSet(psm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods.Parent as Protein)); foreach (var proteinWithDigestInfo in proteinToPeptideInfo) { @@ -209,7 +209,7 @@ private List RunProteinParsimonyEngine() _fdrFilteredPeptides.Add(pep); } - psm.AddProteinMatch((proteinWithDigestInfo.Value.Notch, pep), mfi); + psm.AddProteinMatch(new(proteinWithDigestInfo.Value.Notch, pep, mfi)); } } } @@ -432,7 +432,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 d1459cc87..4e09455a5 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 4c31717e9..ccc5ed148 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 42d8cff0d..0aaf997d9 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 1fa0761a0..e0de99817 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -10,6 +10,7 @@ using System; using EngineLayer.CrosslinkSearch; using EngineLayer.Util; +using EngineLayer.SpectrumMatch; namespace EngineLayer { @@ -19,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; @@ -109,7 +110,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(); } } @@ -118,7 +119,7 @@ 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(); } } @@ -131,9 +132,9 @@ public List PrecursorMassErrorPpm // 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; + protected List _BestMatchingBioPolymersWithSetMods; - public IEnumerable<(int Notch, IBioPolymerWithSetMods Peptide)> BestMatchingBioPolymersWithSetMods + public IEnumerable BestMatchingBioPolymersWithSetMods { get { @@ -142,8 +143,8 @@ public List PrecursorMassErrorPpm // Order by descending sorts things from high (better matches) to low (worse matches) return _BestMatchingBioPolymersWithSetMods.OrderBy(t => - (t.Notch, t.Pwsm, BioPolymersWithSetModsToMatchingFragments.TryGetValue(t.Pwsm, out var ions) ? ions : null), - comparer: BioPolymerNotchFragmentIonComparer); + (t.Notch, t.WithSetMods, t.MatchedIons, + comparer: BioPolymerNotchFragmentIonComparer)); } } @@ -152,7 +153,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) { @@ -166,7 +167,7 @@ public void AddOrReplace(IBioPolymerWithSetMods pwsm, double newScore, int notch } else if (newScore - Score > -ToleranceForScoreDifferentiation && reportAllAmbiguity) //else if the same score and ambiguity is allowed { - _BestMatchingBioPolymersWithSetMods.Add((notch, pwsm)); + _BestMatchingBioPolymersWithSetMods.Add(new(notch, pwsm, matchedFragmentIons)); BioPolymersWithSetModsToMatchingFragments.TryAdd(pwsm, matchedFragmentIons); } else if (newScore - RunnerUpScore > ToleranceForScoreDifferentiation) @@ -176,13 +177,13 @@ 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); + //if (!_BestMatchingBioPolymersWithSetMods.Any(x => x.WithSetMods.Equals(pwsm))) + //{ + // BioPolymersWithSetModsToMatchingFragments.Remove(pwsm); + //} this.ResolveAllAmbiguities(); } @@ -196,40 +197,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.WithSetMods.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.WithSetMods.Parent.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.WithSetMods.FullSequence == hit.Key && p.WithSetMods.Parent.IsDecoy); removedPeptides = true; } } @@ -245,7 +240,7 @@ public void ResolveAllAmbiguities() // 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)) + else if (BioPolymersWithSetModsToMatchingFragments.TryGetValue(_BestMatchingBioPolymersWithSetMods.First().WithSetMods, out var ionList)) MatchedFragmentIons = ionList; else MatchedFragmentIons = null; } @@ -324,15 +319,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(); @@ -341,15 +336,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 virtual 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(); } } @@ -358,11 +349,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; diff --git a/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs new file mode 100644 index 000000000..0295eecaa --- /dev/null +++ b/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs @@ -0,0 +1,15 @@ +using Omics; +using System.Collections.Generic; +using Omics.Fragmentation; + +namespace EngineLayer.SpectrumMatch; + +public class TentativeSpectralMatch(int notch, IBioPolymerWithSetMods pwsm, List matchedIons) +{ + 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; +} \ No newline at end of file diff --git a/MetaMorpheus/EngineLayer/Util/AnalyteType.cs b/MetaMorpheus/EngineLayer/Util/AnalyteType.cs index 8b8d754ce..e49318c53 100644 --- a/MetaMorpheus/EngineLayer/Util/AnalyteType.cs +++ b/MetaMorpheus/EngineLayer/Util/AnalyteType.cs @@ -43,7 +43,7 @@ internal class AnalyteTypeData(string spectralMatchLabel, string uniqueFormLabel internal string SpectralMatchExtension { get; init; } = spectralMatchExtension; /// - /// Gets or sets the label for unique forms (e.g. Peptide). + /// Gets or sets the label for unique forms (e.g. WithSetMods). /// internal string UniqueFormLabel { get; init; } = uniqueFormLabel; diff --git a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs index 20abb160b..d82b02d18 100644 --- a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs +++ b/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs @@ -8,13 +8,13 @@ public class BioPolymerNotchFragmentIonComparer : Comparer<(int Notch, IBioPolym { /// /// 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. + /// 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 + 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 diff --git a/MetaMorpheus/TaskLayer/GlycoSearchTask/PostGlycoSearchAnalysisTask.cs b/MetaMorpheus/TaskLayer/GlycoSearchTask/PostGlycoSearchAnalysisTask.cs index a63750858..504aa9a54 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 22de2c920..0039ce2fc 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 3b30ab068..d83198357 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 4b842483d..98a3c29af 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 e66b1813c..a7c351bcf 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 619680eff..d24033e95 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,15 +571,16 @@ 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)) { diff --git a/MetaMorpheus/TaskLayer/XLSearchTask/WriteXlFile.cs b/MetaMorpheus/TaskLayer/XLSearchTask/WriteXlFile.cs index 65800667a..f2688f1cf 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 9cc93daa5..d57e21e00 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); + var maxPsmData = pepEngine.CreateOnePsmDataEntry("standard", maxScorePsm, bestMatch, !bestMatch.IsDecoy); Assert.That(maxScorePsm.BioPolymersWithSetModsToMatchingFragments.Count - 1, Is.EqualTo(maxPsmData.Ambiguity)); - double normalizationFactor = (double)pwsm.BaseSequence.Length; + 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)); @@ -281,7 +281,7 @@ public static void TestComputePEPValue() string metrics = pepEngine.ComputePEPValuesForAllPSMs(); Assert.That(32 >= trueCount); - //Test Variant Peptide as Input is identified as such as part of PEP calculation input much of the next several lines simply necessry to create a psm. + //Test Variant WithSetMods as Input is identified as such as part of PEP calculation input much of the next several lines simply necessry to create a psm. var anMzSpectrum = new MzSpectrum(new double[] { 1, 1 }, new double[] { 2, 2 }, true); Ms2ScanWithSpecificMass scan = new Ms2ScanWithSpecificMass(new MsDataScan(anMzSpectrum, 1, 1, true, Polarity.Negative, 2, null, "", MZAnalyzerType.Orbitrap, 2, null, null, null), 1, 1, "path", new CommonParameters()); @@ -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,7 +467,7 @@ public static void TestComputePEPValueTopDown() } } - var maxPsmData = pepEngine.CreateOnePsmDataEntry("top-down", maxScorePsm, pwsm, notch, !pwsm.Parent.IsDecoy); + var maxPsmData = pepEngine.CreateOnePsmDataEntry("top-down", maxScorePsm, bestMatch, !bestMatch.WithSetMods.Parent.IsDecoy); Assert.That(maxScorePsm.BioPolymersWithSetModsToMatchingFragments.Count - 1, Is.EqualTo(maxPsmData.Ambiguity)); double normalizationFactor = 1; float maxPsmDeltaScore = (float)Math.Round(maxScorePsm.DeltaScore / normalizationFactor * 10.0, 0); @@ -475,8 +475,8 @@ public static void TestComputePEPValueTopDown() 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/GPTMDengineTest.cs b/MetaMorpheus/Test/GPTMDengineTest.cs index e23d19f4d..89b4f9f22 100644 --- a/MetaMorpheus/Test/GPTMDengineTest.cs +++ b/MetaMorpheus/Test/GPTMDengineTest.cs @@ -273,7 +273,7 @@ public static void TestSearchPtmVariantDatabase() var digestedList = variantProteins[0].GetVariantProteins()[0].Digest(task1.CommonParameters.DigestionParams, new List(), variableModifications).ToList(); Assert.That(digestedList.Count, Is.EqualTo(4)); - //Set Peptide with 1 mod at position 3 + //Set WithSetMods with 1 mod at position 3 PeptideWithSetModifications pepWithSetMods1 = digestedList[1]; //Finally Write MZML file diff --git a/MetaMorpheus/Test/MatchIonsOfAllCharges.cs b/MetaMorpheus/Test/MatchIonsOfAllCharges.cs index 7388b3d02..64e7333f2 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/MultiProteaseParsimonyTest.cs b/MetaMorpheus/Test/MultiProteaseParsimonyTest.cs index b9220d789..ff5b8ffe0 100644 --- a/MetaMorpheus/Test/MultiProteaseParsimonyTest.cs +++ b/MetaMorpheus/Test/MultiProteaseParsimonyTest.cs @@ -772,7 +772,7 @@ public static void TestPSMFdrFiltering_RealFile() /// /// In this test, the peptide sequence ABC results in a unique peptide for protein 1 when the sample is digested with protease alpha. /// But when the sample is digested with protease beta the base sequece ABC is a shared peptide between protein 2 and 4. - /// Peptide EFG is shared between protein 3 and 4. This is a more complex testing set to ensure that Parsing of shared peptides when unique proteins + /// WithSetMods EFG is shared between protein 3 and 4. This is a more complex testing set to ensure that Parsing of shared peptides when unique proteins /// are present is being handled correctly. /// The protein list should contain protein 1 and the protein 4. /// diff --git a/MetaMorpheus/Test/PsmTsvWriterTests.cs b/MetaMorpheus/Test/PsmTsvWriterTests.cs index d82f9a50f..12c272fc8 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, []); + 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 afbfa0b0b..e43b676ca 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/SilacTest.cs b/MetaMorpheus/Test/SilacTest.cs index 8ca9d64c6..8e2b43381 100644 --- a/MetaMorpheus/Test/SilacTest.cs +++ b/MetaMorpheus/Test/SilacTest.cs @@ -216,7 +216,7 @@ public static void TestSilacQuantification() Assert.That(theStringResult.Contains("All target PSMs with q-value <= 0.01: 2")); //it's not a psm, it's a MBR feature. 2 because there are two files, but not 4 because MBR != psm - ///Normal Peptide + ///Normal WithSetMods //test proteins string[] output = File.ReadAllLines(TestContext.CurrentContext.TestDirectory + @"\TestSilac\AllQuantifiedProteinGroups.tsv"); Assert.That(output.Length, Is.EqualTo(2)); diff --git a/MetaMorpheus/Test/SpectralRecoveryTest.cs b/MetaMorpheus/Test/SpectralRecoveryTest.cs index ef80f135e..e0a97c54b 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/TestOGlyco.cs b/MetaMorpheus/Test/TestOGlyco.cs index 9822fb853..25a8fdc8a 100644 --- a/MetaMorpheus/Test/TestOGlyco.cs +++ b/MetaMorpheus/Test/TestOGlyco.cs @@ -424,7 +424,7 @@ public static void OGlycoTest_Localization() [Test] public static void OGlycoTest_Localization2() { - //There may have a bug that MM cannot identify Peptide modified with (HexNAc), This is to test and find the bug. + //There may have a bug that MM cannot identify WithSetMods modified with (HexNAc), This is to test and find the bug. //Get glycanBox var glycanBox = OGlycanBoxes[0]; diff --git a/MetaMorpheus/Test/TestPsm.cs b/MetaMorpheus/Test/TestPsm.cs index 0f8fdb211..66864de2b 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(psm.BioPolymersWithSetModsToMatchingFragments, 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(); diff --git a/MetaMorpheus/Test/XLTest.cs b/MetaMorpheus/Test/XLTest.cs index e8152324a..6f3dc4e43 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")); diff --git a/MetaMorpheus/Test/gptmdPrunedBdTests.cs b/MetaMorpheus/Test/gptmdPrunedBdTests.cs index 39a94b3e2..47e21d6a4 100644 --- a/MetaMorpheus/Test/gptmdPrunedBdTests.cs +++ b/MetaMorpheus/Test/gptmdPrunedBdTests.cs @@ -154,7 +154,7 @@ public static void TestPrunedDatabase() variableModifications).ToList(); Assert.That(digestedList.Count, Is.EqualTo(4)); - //Set Peptide with 1 mod at position 3 + //Set WithSetMods with 1 mod at position 3 PeptideWithSetModifications pepWithSetMods1 = digestedList[1]; //Finally Write MZML file @@ -283,7 +283,7 @@ public static void TestUserModSelectionInPrunedDB() var protein = ProteinDbLoader.LoadProteinXML(xmlName2, true, DecoyType.Reverse, new List(), false, new List(), out Dictionary ok); var digestedList = protein[0].Digest(task5.CommonParameters.DigestionParams, fixedModifications, variableModifications).ToList(); - //Set Peptide with 1 mod at position 3 + //Set WithSetMods with 1 mod at position 3 PeptideWithSetModifications pepWithSetMods1 = digestedList[0]; PeptideWithSetModifications pepWithSetMods2 = digestedList[1]; PeptideWithSetModifications pepWithSetMods3 = digestedList[2]; From cdbbb098f5ff6ad10ed1ee85a847ee9c6561a210 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 00:39:07 -0600 Subject: [PATCH 11/20] SpectralMatch Bug Fix with Comparer --- MetaMorpheus/EngineLayer/SpectralMatch.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MetaMorpheus/EngineLayer/SpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectralMatch.cs index e0de99817..221ba136c 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -143,8 +143,8 @@ public IEnumerable BestMatchingBioPolymersWithSetMods // Order by descending sorts things from high (better matches) to low (worse matches) return _BestMatchingBioPolymersWithSetMods.OrderBy(t => - (t.Notch, t.WithSetMods, t.MatchedIons, - comparer: BioPolymerNotchFragmentIonComparer)); + (t.Notch, t.WithSetMods, t.MatchedIons), + comparer: BioPolymerNotchFragmentIonComparer); } } From 7dc061420d3aacbf6e02ab3b89162eebd92e3d73 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 00:56:19 -0600 Subject: [PATCH 12/20] SpectralMatchQueue --- .../UtilitiesTest/SpectralMatchQueueTests.cs | 381 ++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 MetaMorpheus/Test/UtilitiesTest/SpectralMatchQueueTests.cs diff --git a/MetaMorpheus/Test/UtilitiesTest/SpectralMatchQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/SpectralMatchQueueTests.cs new file mode 100644 index 000000000..1894fbf76 --- /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)); + } + } +} From a13368a5378c312dffb2a122e6d970836e646065 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 00:57:26 -0600 Subject: [PATCH 13/20] Fixed SpectralMatchMatched Ions --- MetaMorpheus/EngineLayer/SpectralMatch.cs | 22 +++++++------ .../SpectrumMatch/TentativeSpectralMatch.cs | 32 +++++++++++++++++-- MetaMorpheus/Test/PsmTsvWriterTests.cs | 2 +- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/MetaMorpheus/EngineLayer/SpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectralMatch.cs index 221ba136c..b95096776 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -180,10 +180,10 @@ public void AddOrReplace(IBioPolymerWithSetMods pwsm, double newScore, int notch public void RemoveThisAmbiguousPeptide(TentativeSpectralMatch tentativeSpectralMatch) { _BestMatchingBioPolymersWithSetMods.Remove(tentativeSpectralMatch); - //if (!_BestMatchingBioPolymersWithSetMods.Any(x => x.WithSetMods.Equals(pwsm))) - //{ - // BioPolymersWithSetModsToMatchingFragments.Remove(pwsm); - //} + if (!_BestMatchingBioPolymersWithSetMods.Any(x => x.WithSetMods.Equals(tentativeSpectralMatch.WithSetMods))) + { + BioPolymersWithSetModsToMatchingFragments.Remove(tentativeSpectralMatch.WithSetMods); + } this.ResolveAllAmbiguities(); } @@ -216,15 +216,15 @@ public void ResolveAllAmbiguities() if (IsDecoy) { bool removedPeptides = false; - var hits = _BestMatchingBioPolymersWithSetMods.GroupBy(p => p.WithSetMods.FullSequence); + var hits = _BestMatchingBioPolymersWithSetMods.GroupBy(p => p.FullSequence); foreach (var hit in hits) { - if (hit.Any(p => p.WithSetMods.Parent.IsDecoy) && hit.Any(p => !p.WithSetMods.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 - _BestMatchingBioPolymersWithSetMods.RemoveAll(p => p.WithSetMods.FullSequence == hit.Key && p.WithSetMods.Parent.IsDecoy); + _BestMatchingBioPolymersWithSetMods.RemoveAll(p => p.FullSequence == hit.Key && p.IsDecoy); removedPeptides = true; } } @@ -336,11 +336,15 @@ public void TrimProteinMatches(HashSet parsimoniousProteins) /// /// This method is used by protein parsimony to add PeptideWithSetModifications objects for modification-agnostic parsimony /// - public virtual void AddProteinMatch(TentativeSpectralMatch tentativeSpectralMatch) + public void AddProteinMatch(TentativeSpectralMatch tentativeSpectralMatch) { if (!_BestMatchingBioPolymersWithSetMods.Contains(tentativeSpectralMatch)) { - _BestMatchingBioPolymersWithSetMods.Add(tentativeSpectralMatch); + _BestMatchingBioPolymersWithSetMods.Add(tentativeSpectralMatch); + if (!BioPolymersWithSetModsToMatchingFragments.ContainsKey(tentativeSpectralMatch.WithSetMods)) + { + BioPolymersWithSetModsToMatchingFragments.Add(tentativeSpectralMatch.WithSetMods, tentativeSpectralMatch.MatchedIons); + } ResolveAllAmbiguities(); } } diff --git a/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs index 0295eecaa..93b06deda 100644 --- a/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectrumMatch/TentativeSpectralMatch.cs @@ -1,10 +1,18 @@ -using Omics; +#nullable enable +using Omics; using System.Collections.Generic; using Omics.Fragmentation; +using System; namespace EngineLayer.SpectrumMatch; -public class TentativeSpectralMatch(int notch, IBioPolymerWithSetMods pwsm, List matchedIons) +/// +/// 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; @@ -12,4 +20,24 @@ public class TentativeSpectralMatch(int notch, IBioPolymerWithSetMods pwsm, List 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/Test/PsmTsvWriterTests.cs b/MetaMorpheus/Test/PsmTsvWriterTests.cs index 12c272fc8..4c90ad520 100644 --- a/MetaMorpheus/Test/PsmTsvWriterTests.cs +++ b/MetaMorpheus/Test/PsmTsvWriterTests.cs @@ -83,7 +83,7 @@ public static void ResolveModificationsTest() Assert.That(matchedIonSeries, Is.EqualTo("[(b1-5.00)+1]")); //removing one of the peptides to reset for the next test - var tentativeSpectralMatch = new TentativeSpectralMatch(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); From 450ec8e4a8db08a4b72bb60c5d1c9a9093acc2cf Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 01:24:36 -0600 Subject: [PATCH 14/20] Removed Erroneous Changes --- MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs | 6 +++--- MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs | 2 +- .../NonSpecificEnzymeSearchEngine.cs | 4 ++-- MetaMorpheus/EngineLayer/SpectralMatch.cs | 2 +- .../BioPolymerNotchFragmentIonComparer.cs | 0 MetaMorpheus/EngineLayer/Util/AnalyteType.cs | 2 +- MetaMorpheus/Test/FdrTest.cs | 2 +- MetaMorpheus/Test/GPTMDengineTest.cs | 2 +- MetaMorpheus/Test/MultiProteaseParsimonyTest.cs | 2 +- MetaMorpheus/Test/SilacTest.cs | 2 +- MetaMorpheus/Test/TestOGlyco.cs | 2 +- MetaMorpheus/Test/gptmdPrunedBdTests.cs | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) rename MetaMorpheus/EngineLayer/{Util => SpectrumMatch}/BioPolymerNotchFragmentIonComparer.cs (100%) diff --git a/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs b/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs index d057391b7..d6fcc5cc0 100644 --- a/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs +++ b/MetaMorpheus/EngineLayer/FdrAnalysis/FdrAnalysisEngine.cs @@ -150,7 +150,7 @@ private void DoFalseDiscoveryRateAnalysis(FdrAnalysisResults myAnalysisResults) /// /// This methods assumes that PSMs are already sorted appropriately for downstream usage /// Then, it counts the number of targets and (fractional) decoys, writes those values to the - /// appropriate FdrInfo (PSM or WithSetMods level), and calculates q-values + /// appropriate FdrInfo (PSM or Peptide level), and calculates q-values /// public void CalculateQValue(List psms, bool peptideLevelCalculation, bool pepCalculation = false) { @@ -167,7 +167,7 @@ public void CalculateQValue(List psms, bool peptideLevelCalculati // Stop if canceled if (GlobalVariables.StopLoops) { break; } - // we have to keep track of q-values separately for each Notch + // we have to keep track of q-values separately for each notch int notch = psm.Notch ?? MassDiffAcceptorNumNotches; if (psm.IsDecoy) { @@ -256,7 +256,7 @@ private void QValueInverted(List psms, bool peptideLevelAnalysis) if (GlobalVariables.StopLoops) { break; } int notch = psms[i].Notch ?? MassDiffAcceptorNumNotches; - // populate the highest q-Value for each Notch + // populate the highest q-Value for each notch if (!qValueNotchCalculated[notch]) { qValueNotch[notch] = (psms[0].GetFdrInfo(peptideLevelAnalysis).CumulativeDecoyNotch + 1) / psms[0].GetFdrInfo(peptideLevelAnalysis).CumulativeTargetNotch; diff --git a/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs b/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs index 650b978b5..9c7454191 100644 --- a/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs +++ b/MetaMorpheus/EngineLayer/Localization/LocalizationEngine.cs @@ -12,7 +12,7 @@ namespace EngineLayer.Localization /// /// The mass difference between an experimental precursor and the theoretical mass of the assigned peptide is determined. The LocalizationEngine attempts /// to localize this mass to one of the residues. It does this by adding the mass difference to each theoretical ion mass and looking for additional matches - /// in the experimental spectrum. This engine should only be run for open, Notch or custom searches. It should not be run for exact mass or missed + /// in the experimental spectrum. This engine should only be run for open, notch or custom searches. It should not be run for exact mass or missed /// monoisopic searches. /// public class LocalizationEngine : MetaMorpheusEngine diff --git a/MetaMorpheus/EngineLayer/NonSpecificEnzymeSearch/NonSpecificEnzymeSearchEngine.cs b/MetaMorpheus/EngineLayer/NonSpecificEnzymeSearch/NonSpecificEnzymeSearchEngine.cs index 9f33ecb05..ff18c3c27 100644 --- a/MetaMorpheus/EngineLayer/NonSpecificEnzymeSearch/NonSpecificEnzymeSearchEngine.cs +++ b/MetaMorpheus/EngineLayer/NonSpecificEnzymeSearch/NonSpecificEnzymeSearchEngine.cs @@ -335,7 +335,7 @@ private Tuple Accepts(List fragments, { foreach (Modification mod in terminalModsAtThisIndex) { - notch = searchMode.Accepts(scanPrecursorMass, theoMass + mod.MonoisotopicMass.Value); //overwrite the Notch, since the other Notch wasn't accepted + notch = searchMode.Accepts(scanPrecursorMass, theoMass + mod.MonoisotopicMass.Value); //overwrite the notch, since the other notch wasn't accepted if (notch >= 0) { terminalMod = mod; @@ -408,7 +408,7 @@ private Tuple Accepts(List fragments, { foreach (Modification terminalMod in terminalModsAtThisIndex) { - notch = searchMode.Accepts(scanPrecursorMass, totalMass + terminalMod.MonoisotopicMass.Value); //overwrite the Notch, since the other Notch wasn't accepted + notch = searchMode.Accepts(scanPrecursorMass, totalMass + terminalMod.MonoisotopicMass.Value); //overwrite the notch, since the other notch wasn't accepted if (notch >= 0) { //need to update the mod dictionary and don't want to overwrite the peptide incase it's in other scans diff --git a/MetaMorpheus/EngineLayer/SpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectralMatch.cs index b95096776..c07979bc8 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -141,7 +141,7 @@ public IEnumerable BestMatchingBioPolymersWithSetMods // 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) + // Order high (better matches) to low (worse matches) return _BestMatchingBioPolymersWithSetMods.OrderBy(t => (t.Notch, t.WithSetMods, t.MatchedIons), comparer: BioPolymerNotchFragmentIonComparer); diff --git a/MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs similarity index 100% rename from MetaMorpheus/EngineLayer/Util/BioPolymerNotchFragmentIonComparer.cs rename to MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs diff --git a/MetaMorpheus/EngineLayer/Util/AnalyteType.cs b/MetaMorpheus/EngineLayer/Util/AnalyteType.cs index e49318c53..8b8d754ce 100644 --- a/MetaMorpheus/EngineLayer/Util/AnalyteType.cs +++ b/MetaMorpheus/EngineLayer/Util/AnalyteType.cs @@ -43,7 +43,7 @@ internal class AnalyteTypeData(string spectralMatchLabel, string uniqueFormLabel internal string SpectralMatchExtension { get; init; } = spectralMatchExtension; /// - /// Gets or sets the label for unique forms (e.g. WithSetMods). + /// Gets or sets the label for unique forms (e.g. Peptide). /// internal string UniqueFormLabel { get; init; } = uniqueFormLabel; diff --git a/MetaMorpheus/Test/FdrTest.cs b/MetaMorpheus/Test/FdrTest.cs index d9a717ce6..212e80d1b 100644 --- a/MetaMorpheus/Test/FdrTest.cs +++ b/MetaMorpheus/Test/FdrTest.cs @@ -281,7 +281,7 @@ public static void TestComputePEPValue() string metrics = pepEngine.ComputePEPValuesForAllPSMs(); Assert.That(32 >= trueCount); - //Test Variant WithSetMods as Input is identified as such as part of PEP calculation input much of the next several lines simply necessry to create a psm. + //Test Variant Peptide as Input is identified as such as part of PEP calculation input much of the next several lines simply necessry to create a psm. var anMzSpectrum = new MzSpectrum(new double[] { 1, 1 }, new double[] { 2, 2 }, true); Ms2ScanWithSpecificMass scan = new Ms2ScanWithSpecificMass(new MsDataScan(anMzSpectrum, 1, 1, true, Polarity.Negative, 2, null, "", MZAnalyzerType.Orbitrap, 2, null, null, null), 1, 1, "path", new CommonParameters()); diff --git a/MetaMorpheus/Test/GPTMDengineTest.cs b/MetaMorpheus/Test/GPTMDengineTest.cs index 89b4f9f22..e23d19f4d 100644 --- a/MetaMorpheus/Test/GPTMDengineTest.cs +++ b/MetaMorpheus/Test/GPTMDengineTest.cs @@ -273,7 +273,7 @@ public static void TestSearchPtmVariantDatabase() var digestedList = variantProteins[0].GetVariantProteins()[0].Digest(task1.CommonParameters.DigestionParams, new List(), variableModifications).ToList(); Assert.That(digestedList.Count, Is.EqualTo(4)); - //Set WithSetMods with 1 mod at position 3 + //Set Peptide with 1 mod at position 3 PeptideWithSetModifications pepWithSetMods1 = digestedList[1]; //Finally Write MZML file diff --git a/MetaMorpheus/Test/MultiProteaseParsimonyTest.cs b/MetaMorpheus/Test/MultiProteaseParsimonyTest.cs index ff5b8ffe0..b9220d789 100644 --- a/MetaMorpheus/Test/MultiProteaseParsimonyTest.cs +++ b/MetaMorpheus/Test/MultiProteaseParsimonyTest.cs @@ -772,7 +772,7 @@ public static void TestPSMFdrFiltering_RealFile() /// /// In this test, the peptide sequence ABC results in a unique peptide for protein 1 when the sample is digested with protease alpha. /// But when the sample is digested with protease beta the base sequece ABC is a shared peptide between protein 2 and 4. - /// WithSetMods EFG is shared between protein 3 and 4. This is a more complex testing set to ensure that Parsing of shared peptides when unique proteins + /// Peptide EFG is shared between protein 3 and 4. This is a more complex testing set to ensure that Parsing of shared peptides when unique proteins /// are present is being handled correctly. /// The protein list should contain protein 1 and the protein 4. /// diff --git a/MetaMorpheus/Test/SilacTest.cs b/MetaMorpheus/Test/SilacTest.cs index 8e2b43381..8ca9d64c6 100644 --- a/MetaMorpheus/Test/SilacTest.cs +++ b/MetaMorpheus/Test/SilacTest.cs @@ -216,7 +216,7 @@ public static void TestSilacQuantification() Assert.That(theStringResult.Contains("All target PSMs with q-value <= 0.01: 2")); //it's not a psm, it's a MBR feature. 2 because there are two files, but not 4 because MBR != psm - ///Normal WithSetMods + ///Normal Peptide //test proteins string[] output = File.ReadAllLines(TestContext.CurrentContext.TestDirectory + @"\TestSilac\AllQuantifiedProteinGroups.tsv"); Assert.That(output.Length, Is.EqualTo(2)); diff --git a/MetaMorpheus/Test/TestOGlyco.cs b/MetaMorpheus/Test/TestOGlyco.cs index 25a8fdc8a..9822fb853 100644 --- a/MetaMorpheus/Test/TestOGlyco.cs +++ b/MetaMorpheus/Test/TestOGlyco.cs @@ -424,7 +424,7 @@ public static void OGlycoTest_Localization() [Test] public static void OGlycoTest_Localization2() { - //There may have a bug that MM cannot identify WithSetMods modified with (HexNAc), This is to test and find the bug. + //There may have a bug that MM cannot identify Peptide modified with (HexNAc), This is to test and find the bug. //Get glycanBox var glycanBox = OGlycanBoxes[0]; diff --git a/MetaMorpheus/Test/gptmdPrunedBdTests.cs b/MetaMorpheus/Test/gptmdPrunedBdTests.cs index 47e21d6a4..39a94b3e2 100644 --- a/MetaMorpheus/Test/gptmdPrunedBdTests.cs +++ b/MetaMorpheus/Test/gptmdPrunedBdTests.cs @@ -154,7 +154,7 @@ public static void TestPrunedDatabase() variableModifications).ToList(); Assert.That(digestedList.Count, Is.EqualTo(4)); - //Set WithSetMods with 1 mod at position 3 + //Set Peptide with 1 mod at position 3 PeptideWithSetModifications pepWithSetMods1 = digestedList[1]; //Finally Write MZML file @@ -283,7 +283,7 @@ public static void TestUserModSelectionInPrunedDB() var protein = ProteinDbLoader.LoadProteinXML(xmlName2, true, DecoyType.Reverse, new List(), false, new List(), out Dictionary ok); var digestedList = protein[0].Digest(task5.CommonParameters.DigestionParams, fixedModifications, variableModifications).ToList(); - //Set WithSetMods with 1 mod at position 3 + //Set Peptide with 1 mod at position 3 PeptideWithSetModifications pepWithSetMods1 = digestedList[0]; PeptideWithSetModifications pepWithSetMods2 = digestedList[1]; PeptideWithSetModifications pepWithSetMods3 = digestedList[2]; From d74039d6aeb9a2035fdfdbb1bcd00d97420a500b Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 01:31:08 -0600 Subject: [PATCH 15/20] BpNotch Comparer Null Switcheroo --- .../BioPolymerNotchFragmentIonComparer.cs | 67 +++++++++---------- .../BioPolymerNotchFragmentIonComparerTest.cs | 6 +- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs index d82b02d18..43bbe2ce6 100644 --- a/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs +++ b/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs @@ -2,40 +2,39 @@ using Omics.Fragmentation; using System.Collections.Generic; -namespace EngineLayer.Util +namespace EngineLayer.SpectrumMatch; + +public class BioPolymerNotchFragmentIonComparer : Comparer<(int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons)> { - public class BioPolymerNotchFragmentIonComparer : Comparer<(int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons)> + /// + /// 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) { - /// - /// 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); - } + 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); } -} +} \ No newline at end of file diff --git a/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs b/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs index 673b75800..562c3b8df 100644 --- a/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs +++ b/MetaMorpheus/Test/UtilitiesTest/BioPolymerNotchFragmentIonComparerTest.cs @@ -4,9 +4,9 @@ using Proteomics.ProteolyticDigestion; using System.Collections.Generic; using System.Reflection; -using EngineLayer.Util; using Omics.Modifications; using System.Diagnostics.CodeAnalysis; +using EngineLayer.SpectrumMatch; namespace Test.UtilitiesTest { @@ -105,11 +105,11 @@ 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.LessThan(0)); + 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.GreaterThan(0)); + Assert.That(comparer.Compare(x, y), Is.LessThan(0)); x = (0, null, new List()); y = (0, null, new List()); From 7b07d83bde2aabb195c9da06aa4eddb424d8d5f9 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 01:33:51 -0600 Subject: [PATCH 16/20] BpComparer Tentative Match addition --- MetaMorpheus/EngineLayer/SpectralMatch.cs | 4 +--- .../BioPolymerNotchFragmentIonComparer.cs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/MetaMorpheus/EngineLayer/SpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectralMatch.cs index c07979bc8..4add48f7b 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -142,9 +142,7 @@ public IEnumerable BestMatchingBioPolymersWithSetMods // It might be worth considering stashing the sorted list in a field instead of sorting every time // Order high (better matches) to low (worse matches) - return _BestMatchingBioPolymersWithSetMods.OrderBy(t => - (t.Notch, t.WithSetMods, t.MatchedIons), - comparer: BioPolymerNotchFragmentIonComparer); + return _BestMatchingBioPolymersWithSetMods.OrderBy(p => p, BioPolymerNotchFragmentIonComparer); } } diff --git a/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs b/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs index 43bbe2ce6..c10773dea 100644 --- a/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs +++ b/MetaMorpheus/EngineLayer/SpectrumMatch/BioPolymerNotchFragmentIonComparer.cs @@ -4,7 +4,8 @@ namespace EngineLayer.SpectrumMatch; -public class BioPolymerNotchFragmentIonComparer : Comparer<(int Notch, IBioPolymerWithSetMods Bpwsm, List MatchedIons)> +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. @@ -37,4 +38,14 @@ public override int Compare((int Notch, IBioPolymerWithSetMods Bpwsm, List Date: Sun, 2 Mar 2025 01:34:13 -0600 Subject: [PATCH 17/20] PriorityQueue Change Testing References --- MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs index 2b352de46..fa3707a32 100644 --- a/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs +++ b/MetaMorpheus/Test/UtilitiesTest/PriorityQueueTests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using EngineLayer.SpectrumMatch; namespace Test.UtilitiesTest; From eaa80a161e2a80063c99d04ca82dd6c618f7b799 Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 01:43:47 -0600 Subject: [PATCH 18/20] FragmentDict: Removed External Refereances --- .../FdrAnalysis/PEPAnalysisEngine.cs | 38 +++++++++---------- .../ProteinParsimonyEngine.cs | 5 ++- MetaMorpheus/EngineLayer/SpectralMatch.cs | 8 ++-- .../TaskLayer/SearchTask/SearchTask.cs | 2 + MetaMorpheus/Test/FdrTest.cs | 4 +- MetaMorpheus/Test/TestPsm.cs | 13 ++----- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs b/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs index 8e1e2b215..d6755809f 100644 --- a/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs +++ b/MetaMorpheus/EngineLayer/FdrAnalysis/PEPAnalysisEngine.cs @@ -510,15 +510,15 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, Tenta deltaScore = (float)Math.Round(psm.DeltaScore / normalizationFactor * multiplier, 0); notch = tentativeSpectralMatch.Notch; modCount = Math.Min((float)tentativeSpectralMatch.WithSetMods.AllModsOneIsNterminus.Keys.Count(), 10); - if (psm.BioPolymersWithSetModsToMatchingFragments[tentativeSpectralMatch.WithSetMods]?.Count() > 0) + if (tentativeSpectralMatch.MatchedIons?.Count() > 0) { 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, tentativeSpectralMatch.WithSetMods) / normalizationFactor * multiplier, 0); - complementaryIonCount = (float)Math.Round(SpectralMatch.GetCountComplementaryIons(psm.BioPolymersWithSetModsToMatchingFragments, tentativeSpectralMatch.WithSetMods) / normalizationFactor * multiplier, 0); + 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)) @@ -564,11 +564,11 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, Tenta else { CrosslinkSpectralMatch csm = (CrosslinkSpectralMatch)psm; - PeptideWithSetModifications selectedAlphaPeptide = csm.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods as PeptideWithSetModifications).First(); - PeptideWithSetModifications selectedBetaPeptide = csm.BetaPeptide?.BestMatchingBioPolymersWithSetMods.Select(p => p.WithSetMods 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); @@ -576,17 +576,17 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, Tenta //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; @@ -602,8 +602,8 @@ public PsmData CreateOnePsmDataEntry(string searchType, SpectralMatch psm, Tenta 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); @@ -1017,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/ProteinParsimony/ProteinParsimonyEngine.cs b/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinParsimonyEngine.cs index ce4d9cdf5..0eb450051 100644 --- a/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinParsimonyEngine.cs +++ b/MetaMorpheus/EngineLayer/ProteinParsimony/ProteinParsimonyEngine.cs @@ -185,8 +185,9 @@ private List RunProteinParsimonyEngine() // create any new associations that need to be made foreach (PeptideSpectralMatch psm in baseSequence.Value) { - IBioPolymerWithSetMods originalPeptide = psm.BestMatchingBioPolymersWithSetMods.First().WithSetMods; - List mfi = psm.BioPolymersWithSetModsToMatchingFragments[originalPeptide]; + 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) diff --git a/MetaMorpheus/EngineLayer/SpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectralMatch.cs index 4add48f7b..7bce50629 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -404,10 +404,10 @@ protected SpectralMatch(SpectralMatch psm, List bestMatc /// /// /// - 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(); @@ -534,9 +534,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/TaskLayer/SearchTask/SearchTask.cs b/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs index d24033e95..1b5ed1085 100644 --- a/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs +++ b/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs @@ -585,6 +585,8 @@ public static void MatchInternalFragmentIons(SpectralMatch[] fileSpecificPsms, M if (!PeptidesToMatchingInternalFragments.Contains(currentPwsm)) { PeptidesToMatchingInternalFragments.Add(currentPwsm); //record that we've seen this peptide + + thisPeptide.MatchedIons.AddRange(matchedIonsForAllAmbiguousPeptides[peptideIndex]); //add the matched ions psm.BioPolymersWithSetModsToMatchingFragments[currentPwsm].AddRange(matchedIonsForAllAmbiguousPeptides[peptideIndex]); //add the matched ions } } diff --git a/MetaMorpheus/Test/FdrTest.cs b/MetaMorpheus/Test/FdrTest.cs index 212e80d1b..109c7aee7 100644 --- a/MetaMorpheus/Test/FdrTest.cs +++ b/MetaMorpheus/Test/FdrTest.cs @@ -236,7 +236,7 @@ public static void TestComputePEPValue() } var maxPsmData = pepEngine.CreateOnePsmDataEntry("standard", maxScorePsm, bestMatch, !bestMatch.IsDecoy); - Assert.That(maxScorePsm.BioPolymersWithSetModsToMatchingFragments.Count - 1, Is.EqualTo(maxPsmData.Ambiguity)); + 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)); @@ -468,7 +468,7 @@ public static void TestComputePEPValueTopDown() } var maxPsmData = pepEngine.CreateOnePsmDataEntry("top-down", maxScorePsm, bestMatch, !bestMatch.WithSetMods.Parent.IsDecoy); - Assert.That(maxScorePsm.BioPolymersWithSetModsToMatchingFragments.Count - 1, Is.EqualTo(maxPsmData.Ambiguity)); + 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)); diff --git a/MetaMorpheus/Test/TestPsm.cs b/MetaMorpheus/Test/TestPsm.cs index 66864de2b..1949a55dc 100644 --- a/MetaMorpheus/Test/TestPsm.cs +++ b/MetaMorpheus/Test/TestPsm.cs @@ -207,7 +207,7 @@ public static void TestLongestFragmentIonSequence() { foreach (var bestMatch in psm.BestMatchingBioPolymersWithSetMods) { - longestSeriesObserved.Add(SpectralMatch.GetLongestIonSeriesBidirectional(psm.BioPolymersWithSetModsToMatchingFragments, bestMatch.WithSetMods)); + longestSeriesObserved.Add(SpectralMatch.GetLongestIonSeriesBidirectional(bestMatch.MatchedIons, bestMatch.WithSetMods)); } } } @@ -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)); } From cbdc8204e301343af3c1d98d57589c81a91ac6cb Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 01:59:43 -0600 Subject: [PATCH 19/20] TentativeSpectralMatch Testing --- .../TentativeSpectralMatchTest.cs | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 MetaMorpheus/Test/UtilitiesTest/TentativeSpectralMatchTest.cs diff --git a/MetaMorpheus/Test/UtilitiesTest/TentativeSpectralMatchTest.cs b/MetaMorpheus/Test/UtilitiesTest/TentativeSpectralMatchTest.cs new file mode 100644 index 000000000..33355a9d0 --- /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))); + } + } +} From 2263343abf0c065797429a88ec7adfd0b2b7202c Mon Sep 17 00:00:00 2001 From: nbollis Date: Sun, 2 Mar 2025 02:06:37 -0600 Subject: [PATCH 20/20] FragmentDict: Removed Entirely --- MetaMorpheus/EngineLayer/SpectralMatch.cs | 26 +++---------------- .../TaskLayer/SearchTask/SearchTask.cs | 1 - 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/MetaMorpheus/EngineLayer/SpectralMatch.cs b/MetaMorpheus/EngineLayer/SpectralMatch.cs index 7bce50629..22243f8ee 100644 --- a/MetaMorpheus/EngineLayer/SpectralMatch.cs +++ b/MetaMorpheus/EngineLayer/SpectralMatch.cs @@ -35,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; @@ -127,11 +126,6 @@ public List PrecursorMassErrorPpm public DigestionParams DigestionParams { get; } public static BioPolymerNotchFragmentIonComparer BioPolymerNotchFragmentIonComparer = new(); - - // 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 _BestMatchingBioPolymersWithSetMods; public IEnumerable BestMatchingBioPolymersWithSetMods @@ -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(new(notch, pwsm, matchedFragmentIons)); - BioPolymersWithSetModsToMatchingFragments.TryAdd(pwsm, matchedFragmentIons); } else if (newScore - RunnerUpScore > ToleranceForScoreDifferentiation) { @@ -178,10 +168,6 @@ public void AddOrReplace(IBioPolymerWithSetMods pwsm, double newScore, int notch public void RemoveThisAmbiguousPeptide(TentativeSpectralMatch tentativeSpectralMatch) { _BestMatchingBioPolymersWithSetMods.Remove(tentativeSpectralMatch); - if (!_BestMatchingBioPolymersWithSetMods.Any(x => x.WithSetMods.Equals(tentativeSpectralMatch.WithSetMods))) - { - BioPolymersWithSetModsToMatchingFragments.Remove(tentativeSpectralMatch.WithSetMods); - } this.ResolveAllAmbiguities(); } @@ -237,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().WithSetMods, 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) @@ -339,10 +324,6 @@ public void AddProteinMatch(TentativeSpectralMatch tentativeSpectralMatch) if (!_BestMatchingBioPolymersWithSetMods.Contains(tentativeSpectralMatch)) { _BestMatchingBioPolymersWithSetMods.Add(tentativeSpectralMatch); - if (!BioPolymersWithSetModsToMatchingFragments.ContainsKey(tentativeSpectralMatch.WithSetMods)) - { - BioPolymersWithSetModsToMatchingFragments.Add(tentativeSpectralMatch.WithSetMods, tentativeSpectralMatch.MatchedIons); - } ResolveAllAmbiguities(); } } @@ -387,7 +368,6 @@ protected SpectralMatch(SpectralMatch psm, List bestMatc IsDecoy = psm.IsDecoy; IsContaminant = psm.IsContaminant; DigestionParams = psm.DigestionParams; - BioPolymersWithSetModsToMatchingFragments = psm.BioPolymersWithSetModsToMatchingFragments; SpectralAngle = psm.SpectralAngle; } diff --git a/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs b/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs index 1b5ed1085..4fccf3709 100644 --- a/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs +++ b/MetaMorpheus/TaskLayer/SearchTask/SearchTask.cs @@ -587,7 +587,6 @@ public static void MatchInternalFragmentIons(SpectralMatch[] fileSpecificPsms, M PeptidesToMatchingInternalFragments.Add(currentPwsm); //record that we've seen this peptide thisPeptide.MatchedIons.AddRange(matchedIonsForAllAmbiguousPeptides[peptideIndex]); //add the matched ions - psm.BioPolymersWithSetModsToMatchingFragments[currentPwsm].AddRange(matchedIonsForAllAmbiguousPeptides[peptideIndex]); //add the matched ions } } }