Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IEquality Hotfix #817

Merged
merged 5 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -28,6 +28,15 @@
char this[int zeroBasedIndex] => BaseSequence[zeroBasedIndex];

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
Loading