Skip to content

Commit

Permalink
IEquality Hotfix (#817)
Browse files Browse the repository at this point in the history
* fixed bugs

* More Equality Tests

* One More Time

* Digestion Agent Equality and additional comments

* updated protease and expanded test coverage

---------

Co-authored-by: Nic Bollis <nbollis@wisc.edu>
  • Loading branch information
nbollis and Nic Bollis authored Dec 16, 2024
1 parent 21c1702 commit d98326b
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 82 deletions.
17 changes: 11 additions & 6 deletions mzLib/Omics/Digestion/DigestionAgent.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using Omics.Modifications;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Omics.Digestion
{
Expand All @@ -17,7 +12,7 @@ protected DigestionAgent(string name, CleavageSpecificity cleavageSpecificity, L
CleavageMod = cleavageMod;
}

public string Name { get; init; }
public readonly string Name;
public CleavageSpecificity CleavageSpecificity { get; init; }
public List<DigestionMotif> DigestionMotifs { get; init; }
public Modification CleavageMod { get; set; }
Expand All @@ -27,6 +22,16 @@ public override string ToString()
return Name;
}

public override bool Equals(object? obj)
{
return obj is DigestionAgent agent && agent.Name == Name;
}

public override int GetHashCode()
{
return Name.GetHashCode();
}

/// <summary>
/// Is length of given peptide okay, given minimum and maximum?
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions mzLib/Omics/IBioPolymer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,14 @@ public interface IBioPolymer : IEquatable<IBioPolymer>

IEnumerable<IBioPolymerWithSetMods> Digest(IDigestionParams digestionParams, List<Modification> allKnownFixedModifications,
List<Modification> variableModifications, List<SilacLabel> silacLabels = null, (SilacLabel startLabel, SilacLabel endLabel)? turnoverLabels = null, bool topDownTruncationSearch = false);

Check warning on line 31 in mzLib/Omics/IBioPolymer.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 31 in mzLib/Omics/IBioPolymer.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

bool IEquatable<IBioPolymer>.Equals(IBioPolymer? other)
{
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
if (other.GetType() != GetType()) return false;
return Accession == other.Accession
&& BaseSequence == other.BaseSequence;
}
}
}
28 changes: 23 additions & 5 deletions mzLib/Omics/IBioPolymerWithSetMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ public interface IBioPolymerWithSetMods : IHasChemicalFormula, IEquatable<IBioPo
char this[int zeroBasedIndex] => BaseSequence[zeroBasedIndex];
IBioPolymer Parent { get; }

/// <summary>
/// Default Equals Method for IBioPolymerWithSetMods
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
/// <remarks>
/// Different parent but same sequence and digestion condition => are equal
/// Different Digestion agent but same sequence => are not equal (this is for multi-protease analysis in MetaMorpheus)
/// </remarks>
bool IEquatable<IBioPolymerWithSetMods>.Equals(IBioPolymerWithSetMods? other)
{
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
if (other.GetType() != GetType()) return false;

// for those constructed from sequence and mods only
if (Parent is null && other.Parent is null)
return FullSequence.Equals(other.FullSequence);

return FullSequence == other.FullSequence
&& Equals(DigestionParams?.DigestionAgent, other.DigestionParams?.DigestionAgent);
}

public void Fragment(DissociationType dissociationType, FragmentationTerminus fragmentationTerminus,
List<Product> products);

Expand Down Expand Up @@ -163,10 +186,5 @@ public static Dictionary<int, Modification> GetModificationDictionaryFromFullSeq
/// <returns></returns>
public static List<Modification> GetModificationsFromFullSequence(string fullSequence,
Dictionary<string, Modification> allModsKnown) => [.. GetModificationDictionaryFromFullSequence(fullSequence, allModsKnown).Values];

public bool Equals(IBioPolymerWithSetMods other);

public int GetHashCode();

}
}
16 changes: 9 additions & 7 deletions mzLib/Proteomics/Protein/Protein.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace Proteomics
{
public class Protein : IBioPolymer
public class Protein : IBioPolymer, IEquatable<Protein>
{
private List<ProteolysisProduct> _proteolysisProducts;

Expand Down Expand Up @@ -969,16 +969,18 @@ public int CompareTo(Protein other)
//not sure if we require any additional fields for equality
public override bool Equals(object obj)
{
Protein otherProtein = (Protein)obj;
return otherProtein != null && otherProtein.Accession.Equals(Accession) && otherProtein.BaseSequence.Equals(BaseSequence);
if (obj is Protein bioPol)
{
return Equals(bioPol);
}
return false;
}

public bool Equals(IBioPolymer other)
public bool Equals(Protein other)
{
Protein otherProtein = (Protein)other;
return otherProtein != null && otherProtein.Accession.Equals(Accession) && otherProtein.BaseSequence.Equals(BaseSequence);
return (this as IBioPolymer).Equals(other);
}

/// <summary>
/// The protein object uses the default hash code method for speed,
/// but note that two protein objects with the same information will give two different hash codes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace Proteomics.ProteolyticDigestion
{
[Serializable]
public class PeptideWithSetModifications : ProteolyticPeptide, IBioPolymerWithSetMods
public class PeptideWithSetModifications : ProteolyticPeptide, IBioPolymerWithSetMods, IEquatable<PeptideWithSetModifications>
{
public string FullSequence { get; private set; } //sequence with modifications
public int NumFixedMods { get; }
Expand Down Expand Up @@ -886,30 +886,29 @@ public override string ToString()

public override bool Equals(object obj)
{
var q = obj as PeptideWithSetModifications;
if (q == null) return false;
return Equals(q);
if (obj is PeptideWithSetModifications peptide)
{
return Equals(peptide);
}
return false;
}

public bool Equals(IBioPolymerWithSetMods other)
public bool Equals(PeptideWithSetModifications other)
{
return FullSequence == other.FullSequence
&& OneBasedStartResidue == other.OneBasedStartResidue
&& ((Parent != null && Parent.Equals(other.Parent)) || (Parent == null & other.Parent == null))
&& ((DigestionParams?.DigestionAgent != null && DigestionParams.DigestionAgent.Equals(other.DigestionParams?.DigestionAgent))
|| (DigestionParams?.DigestionAgent == null & other.DigestionParams?.DigestionAgent == null));
// interface equals first because it does null and reference checks
return (this as IBioPolymerWithSetMods).Equals(other)
&& OneBasedStartResidue == other!.OneBasedStartResidue
&& Equals(Parent, other.Parent);
}

public override int GetHashCode()
{
var hash = new HashCode();
hash.Add(FullSequence);
hash.Add(OneBasedStartResidue);
if (Parent != null)
if (Parent?.Accession != null)
{
hash.Add(Parent);
if (Parent.Accession != null)
hash.Add(Parent.Accession);
hash.Add(Parent.Accession);
}
if (DigestionParams?.DigestionAgent != null)
{
Expand Down
11 changes: 0 additions & 11 deletions mzLib/Proteomics/ProteolyticDigestion/Protease.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,6 @@ public override string ToString()
return Name;
}

public override bool Equals(object obj)
{
return obj is Protease a
&& (a.Name == null && Name == null || a.Name.Equals(Name));
}

public override int GetHashCode()
{
return (Name ?? "").GetHashCode();
}

/// <summary>
/// This method is used to determine cleavage specificity if the cleavage specificity is unknown
/// This occurs in the speedy nonspecific/semispecific searches when digesting post-search
Expand Down
13 changes: 13 additions & 0 deletions mzLib/Test/DigestionAgentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Omics.Digestion;
using Omics.Modifications;
using System.Collections.Generic;
using NUnit.Framework;

namespace Test
{
public class DigestionAgentTests
{


}
}
44 changes: 44 additions & 0 deletions mzLib/Test/TestDigestionMotif.cs
Original file line number Diff line number Diff line change
Expand Up @@ -595,5 +595,49 @@ public void TestDigestionParamsMaskedProperties()
digestionParams.MaxModsForPeptide = 3;
Assert.That(digestionParams.MaxMods, Is.EqualTo(digestionParams.MaxModsForPeptide));
}

private class TestDigestionAgent : DigestionAgent
{
public TestDigestionAgent(string name, CleavageSpecificity cleavageSpecificity, List<DigestionMotif> motifList, Modification cleavageMod)
: base(name, cleavageSpecificity, motifList, cleavageMod)
{
}
}

[Test]
public void Equals_SameName_ReturnsTrue()
{
var agent1 = ProteaseDictionary.Dictionary["trypsin"];
var agent2 = ProteaseDictionary.Dictionary["trypsin"];

Assert.That(agent1.Equals(agent2), Is.True);
}

[Test]
public void Equals_DifferentName_ReturnsFalse()
{
var agent1 = ProteaseDictionary.Dictionary["trypsin"];
var agent2 = ProteaseDictionary.Dictionary["Arg-C"];

Assert.That(agent1.Equals(agent2), Is.False);
}

[Test]
public void GetHashCode_SameName_ReturnsSameHashCode()
{
var agent1 = ProteaseDictionary.Dictionary["trypsin"];
var agent2 = ProteaseDictionary.Dictionary["trypsin"];

Assert.That(agent1.GetHashCode(), Is.EqualTo(agent2.GetHashCode()));
}

[Test]
public void GetHashCode_DifferentName_ReturnsDifferentHashCode()
{
var agent1 = ProteaseDictionary.Dictionary["trypsin"];
var agent2 = ProteaseDictionary.Dictionary["Arg-C"];

Assert.That(agent1.GetHashCode(), Is.Not.EqualTo(agent2.GetHashCode()));
}
}
}
13 changes: 13 additions & 0 deletions mzLib/Test/TestPeptideWithSetMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Omics.Digestion;
using Omics.Fragmentation;
using Omics.Modifications;
using Transcriptomics.Digestion;
using UsefulProteomicsDatabases;
using Stopwatch = System.Diagnostics.Stopwatch;

Expand Down Expand Up @@ -60,6 +61,18 @@ public static void TestDifferentProteaseEquals()
Assert.That(!pep1.GetHashCode().Equals(pep2.GetHashCode()));
}

[Test]
public static void TestPeptideOligoEquality()
{
var oligo = new OligoWithSetMods("GUACUG", []);
var peptide = new PeptideWithSetModifications("PEPTIDE", []);

Assert.That(!oligo.Equals(peptide));
Assert.That(!peptide.Equals(oligo));
Assert.That(!((IBioPolymerWithSetMods)oligo).Equals(peptide));
Assert.That(!((IBioPolymerWithSetMods)peptide).Equals(oligo));
}

[Test]
public static void TestSemiFewCleavages()
{
Expand Down
40 changes: 40 additions & 0 deletions mzLib/Test/TestProteinProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Omics;
using Omics.Modifications;
using Stopwatch = System.Diagnostics.Stopwatch;
using Transcriptomics;

namespace Test
{
Expand Down Expand Up @@ -261,5 +263,43 @@ public static void TestProteoformClassification()//string inputPath)
///Test case 3 is 2B (not level 3) because you've localized the mod, you just aren't sure what mod it is.
///In test case 1, you know what the mods are, but you're not sure where they belong.
}

[Test]
public void TestProteinEquals()
{
string sequence = "MKWVTFISLLFLFSSAYSRGVFRRDTHKSEIAHRFKDLGEEHFKGLVLIAFSQYLQQCPFDEHVKLVNEVTEFAKTCVADESAENCDKSLHTLFGDELCKVASLRETYGDMADCCEKQEPERNECFLSHKDDSPDLPKLKPDPNTLCDEFKADEKKFWGKYLYEIARRHPYFYAPELLFFAKRYKAAFTECCQAADKAACLLPKLDELRDEGKASSAKQR";
string accession = "P02768";
Protein protein1 = new Protein(sequence, accession);
Protein protein2 = new Protein(sequence, accession);

NUnit.Framework.Assert.That(protein1.Equals(protein2), Is.True);
NUnit.Framework.Assert.That(protein1.Equals((object)protein2), Is.True);
NUnit.Framework.Assert.That(protein1.Equals(null), Is.False);
}

[Test]
public void TestProteinGetHashCode()
{
string sequence = "MKWVTFISLLFLFSSAYSRGVFRRDTHKSEIAHRFKDLGEEHFKGLVLIAFSQYLQQCPFDEHVKLVNEVTEFAKTCVADESAENCDKSLHTLFGDELCKVASLRETYGDMADCCEKQEPERNECFLSHKDDSPDLPKLKPDPNTLCDEFKADEKKFWGKYLYEIARRHPYFYAPELLFFAKRYKAAFTECCQAADKAACLLPKLDELRDEGKASSAKQR";
string accession = "P02768";
Protein protein = new Protein(sequence, accession);

NUnit.Framework.Assert.That(protein.GetHashCode(), Is.EqualTo(sequence.GetHashCode()));
}

[Test]
public void TestProteinRnaEquality()
{
string sequence = "MKWVTFISLLFLFSSAYSRGVFRRDTHKSEIAHRFKDLGEEHFKGLVLIAFSQYLQQCPFDEHVKLVNEVTEFAKTCVADESAENCDKSLHTLFGDELCKVASLRETYGDMADCCEKQEPERNECFLSHKDDSPDLPKLKPDPNTLCDEFKADEKKFWGKYLYEIARRHPYFYAPELLFFAKRYKAAFTECCQAADKAACLLPKLDELRDEGKASSAKQR";
string accession = "P02768";
Protein protein1 = new Protein(sequence, accession);
RNA rna = new RNA("GUACUG");


Assert.That(!rna.Equals(protein1));
Assert.That(!protein1.Equals(rna));
Assert.That(!((IBioPolymer)rna).Equals(protein1));
Assert.That(!((IBioPolymer)protein1).Equals(rna));
}
}
}
Loading

0 comments on commit d98326b

Please sign in to comment.