Skip to content

Commit

Permalink
Merge branch 'master' into DbWriterFix
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander-Sol authored Jan 7, 2025
2 parents b4301cc + dc44773 commit 3fb1b33
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 45 deletions.
23 changes: 0 additions & 23 deletions mzLib/Omics/IBioPolymerWithSetMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,6 @@ 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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Proteomics.ProteolyticDigestion
[Serializable]
public class PeptideWithSetModifications : ProteolyticPeptide, IBioPolymerWithSetMods, IEquatable<PeptideWithSetModifications>
{
public string FullSequence { get; private set; } //sequence with modifications
public string FullSequence { get; init; } //sequence with modifications
public int NumFixedMods { get; }
// Parameter to store the full sequence of the corresponding Target or Decoy peptide
// If the peptide in question is a decoy, this pairs it to the target it was generated from
Expand Down Expand Up @@ -884,6 +884,11 @@ public override string ToString()
return FullSequence + string.Join("\t", AllModsOneIsNterminus.Select(m => m.ToString()));
}

#region IEquatable

/// <summary>
/// Peptides are equal if they have the same full sequence, parent, and digestion agent
/// </summary>
public override bool Equals(object obj)
{
if (obj is PeptideWithSetModifications peptide)
Expand All @@ -893,12 +898,29 @@ public override bool Equals(object obj)
return false;
}

/// <summary>
/// Peptides are equal if they have the same full sequence, parent, and digestion agent
/// </summary>
public bool Equals(IBioPolymerWithSetMods other) => Equals(other as PeptideWithSetModifications);

/// <summary>
/// Peptides are equal if they have the same full sequence, parent, and digestion agent
/// </summary>
public bool Equals(PeptideWithSetModifications other)
{
// interface equals first because it does null and reference checks
return (this as IBioPolymerWithSetMods).Equals(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)
// These last two are important for parsimony in MetaMorpheus
&& OneBasedStartResidue == other!.OneBasedStartResidue
&& Equals(Parent, other.Parent);
&& Equals(Parent?.Accession, other.Parent?.Accession);
}

public override int GetHashCode()
Expand All @@ -917,6 +939,8 @@ public override int GetHashCode()
return hash.ToHashCode();
}

#endregion

/// <summary>
/// This should be run after deserialization of a PeptideWithSetModifications, in order to set its Protein and Modification objects, which were not serialized
/// </summary>
Expand Down
41 changes: 41 additions & 0 deletions mzLib/Test/TestPeptideWithSetMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public static void TestPeptideOligoEquality()
Assert.That(!peptide.Equals(oligo));
Assert.That(!((IBioPolymerWithSetMods)oligo).Equals(peptide));
Assert.That(!((IBioPolymerWithSetMods)peptide).Equals(oligo));
Assert.That(!((object)oligo).Equals(peptide));
Assert.That(!((object)peptide).Equals(oligo));
}

[Test]
Expand Down Expand Up @@ -1194,6 +1196,45 @@ public static void TestPeptideWithSetModsNoParentProtein()
Assert.AreEqual('-', last.NextResidue);
}

[Test]
public static void TestPeptideWithSetModsEquals()
{
// Create two proteins
Protein protein1 = new Protein("SEQUENCEK", "accession1");
Protein protein2 = new Protein("SEQUENCEK", "accession2");

// Create digestion parameters
DigestionParams digestionParams = new DigestionParams(protease: "trypsin", maxMissedCleavages: 0, initiatorMethionineBehavior: InitiatorMethionineBehavior.Retain);

// Digest the proteins to get peptides
PeptideWithSetModifications peptide1 = protein1.Digest(digestionParams, new List<Modification>(), new List<Modification>()).First();
PeptideWithSetModifications peptide2 = protein2.Digest(digestionParams, new List<Modification>(), new List<Modification>()).First();

// Test equality - same peptide
Assert.IsTrue(peptide1.Equals(peptide1));

// different peptide
Assert.IsTrue(!peptide1.Equals(peptide2));
Assert.IsTrue(!peptide1.Equals((object)peptide2));
Assert.IsTrue(!peptide1.Equals((IBioPolymerWithSetMods)peptide2));
Assert.AreNotEqual(peptide1.GetHashCode(), peptide2.GetHashCode());

// Test inequality with different start residue
PeptideWithSetModifications peptide3 = new PeptideWithSetModifications(protein1, digestionParams, 2, 9, CleavageSpecificity.Full, "", 0, new Dictionary<int, Modification>(), 0);
Assert.IsFalse(peptide1.Equals(peptide3));

// Test inequality with different parent accession
PeptideWithSetModifications peptide4 = new PeptideWithSetModifications(protein2, digestionParams, 1, 9, CleavageSpecificity.Full, "", 0, new Dictionary<int, Modification>(), 0);
Assert.IsFalse(peptide1.Equals(peptide4));

// all fail on null
Assert.That(!peptide1.Equals(null));
Assert.That(!peptide1.Equals((object)null));
Assert.That(!peptide1.Equals((PeptideWithSetModifications)null));
}



[Test]
public static void TestIBioPolymerWithSetModsModificationFromFullSequence()
{
Expand Down
40 changes: 26 additions & 14 deletions mzLib/Test/Transcriptomics/TestOligoWithSetMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,43 +91,55 @@ public static void TestEquality()
.Digest(new RnaDigestionParams(), [], [])
.ElementAt(1);

Assert.That(oligoWithSetMods, Is.EqualTo(oligoWithSetMods2));
// same oligos
Assert.That(oligoWithSetMods.Equals(oligoWithSetMods2));
Assert.That(oligoWithSetMods.Equals((object)oligoWithSetMods2));
Assert.That(oligoWithSetMods.Equals((OligoWithSetMods)oligoWithSetMods2));
Assert.That(oligoWithSetMods.Equals(oligoWithSetMods));
Assert.That(oligoWithSetMods.Equals((object)oligoWithSetMods));
Assert.That(oligoWithSetMods.Equals((OligoWithSetMods)oligoWithSetMods));
Assert.That(oligoWithSetMods.GetHashCode(), Is.EqualTo(oligoWithSetMods2.GetHashCode()));
Assert.That(oligoWithSetMods.Equals((object)oligoWithSetMods2)); // Test the Equals(Object obj) method

// all fail on null
Assert.That(!oligoWithSetMods2.Equals(null));
Assert.That(!oligoWithSetMods2.Equals((object)null));
Assert.That(!oligoWithSetMods2.Equals((OligoWithSetMods)null));

// Null parent checks
oligoWithSetMods = new(oligoWithSetMods.FullSequence, modDict.ToDictionary(p => p.Value.First().IdWithMotif, p => p.Value.First()));
oligoWithSetMods2 = new OligoWithSetMods(oligoWithSetMods.FullSequence, modDict.ToDictionary(p => p.Value.First().IdWithMotif, p => p.Value.First()));
var oligoWithSetMods3 = new OligoWithSetMods(oligoWithSetMods.FullSequence + "AGAUA", modDict.ToDictionary(p => p.Value.First().IdWithMotif, p => p.Value.First()));

Assert.That(oligoWithSetMods, Is.EqualTo(oligoWithSetMods2));
Assert.That(oligoWithSetMods, Is.EqualTo((object)oligoWithSetMods2));
Assert.That(oligoWithSetMods, Is.EqualTo((OligoWithSetMods)oligoWithSetMods2));
Assert.That(oligoWithSetMods, Is.Not.EqualTo(oligoWithSetMods3));
Assert.That(oligoWithSetMods, Is.Not.EqualTo((object)oligoWithSetMods3));
Assert.That(oligoWithSetMods, Is.Not.EqualTo((IBioPolymerWithSetMods)oligoWithSetMods3));
// same oligo null parent
Assert.That(oligoWithSetMods.Equals(oligoWithSetMods2));
Assert.That(oligoWithSetMods.Equals((object)oligoWithSetMods2));
Assert.That(oligoWithSetMods.Equals((OligoWithSetMods)oligoWithSetMods2));

// different oligo null parent
Assert.That(!oligoWithSetMods.Equals(oligoWithSetMods3));
Assert.That(!oligoWithSetMods.Equals((object)oligoWithSetMods3));
Assert.That(!oligoWithSetMods.Equals((IBioPolymerWithSetMods)oligoWithSetMods3));
}

[Test]
[TestCase("GUACUG", "GUACUGGUACUG", "RNase A")]
[TestCase("GUAGGAG", "GUAGCAG", "RNase A")]
public static void TestEquality_DifferentParentSameDigestionProduct(string sequence1, string sequence2, string enzyme)
public static void TestInequality_DifferentParentSameDigestionProduct(string sequence1, string sequence2, string enzyme)
{
var digestionParams = new RnaDigestionParams(rnase: enzyme, minLength: 1, maxMissedCleavages: 0);

var oligo1 = new RNA(sequence1)
var oligo1 = new RNA(sequence1, "", "rna1", "", "")
.Digest(digestionParams, [], [])
.First();

var oligo2 = new RNA(sequence2)
var oligo2 = new RNA(sequence2, "", "rna3", "", "")
.Digest(digestionParams, [], [])
.First();

Assert.That(oligo1, Is.EqualTo(oligo2));
Assert.That(oligo1, Is.Not.EqualTo(oligo2));
Assert.That(oligo1.Equals(oligo1));
Assert.That(oligo1, Is.EqualTo((object)oligo2));
Assert.That(oligo1.GetHashCode(), Is.EqualTo(oligo2.GetHashCode()));
Assert.That(oligo1, Is.Not.EqualTo((object)oligo2));
Assert.That(oligo1.GetHashCode(), Is.Not.EqualTo(oligo2.GetHashCode()));
}

/// <summary>
Expand Down
33 changes: 32 additions & 1 deletion mzLib/Transcriptomics/Digestion/OligoWithSetMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ public void Fragment(DissociationType dissociationType, FragmentationTerminus fr
products.AddRange(GetNeutralFragments(type, sequence));
}

#region IEquatable

/// <summary>
/// Oligos are equal if they have the same full sequence, parent, and digestion agent, and terminal caps
/// </summary>
public override bool Equals(object? obj)
{
if (obj is OligoWithSetMods oligo)
Expand All @@ -224,9 +229,31 @@ public override bool Equals(object? obj)
return false;
}

/// <summary>
/// Oligos are equal if they have the same full sequence, parent, and digestion agent, and terminal caps
/// </summary>
public bool Equals(IBioPolymerWithSetMods? other) => Equals(other as OligoWithSetMods);

/// <summary>
/// Oligos are equal if they have the same full sequence, parent, and digestion agent, and terminal caps
/// </summary>
public bool Equals(OligoWithSetMods? other)
{
return (this as IBioPolymerWithSetMods).Equals(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)
&& _fivePrimeTerminus.Equals(other._fivePrimeTerminus)
&& _threePrimeTerminus.Equals(other._threePrimeTerminus)
// These last two are important for parsimony in MetaMorpheus
&& OneBasedStartResidue == other!.OneBasedStartResidue
&& Equals(Parent?.Accession, other.Parent?.Accession);
}

public override int GetHashCode()
Expand All @@ -242,9 +269,13 @@ public override int GetHashCode()
{
hash.Add(DigestionParams.DigestionAgent);
}
hash.Add(FivePrimeTerminus);
hash.Add(ThreePrimeTerminus);
return hash.ToHashCode();
}

#endregion

/// <summary>
/// Generates theoretical internal fragments for given dissociation type for this peptide.
/// The "products" parameter is filled with these fragments.
Expand Down
6 changes: 3 additions & 3 deletions mzLib/Transcriptomics/RNA.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public RNA(string sequence, IHasChemicalFormula? fivePrimeTerm = null, IHasChemi
/// </summary>
/// <param name="sequence"></param>
/// <param name="name"></param>
/// <param name="identifier"></param>
/// <param name="accession"></param>
/// <param name="organism"></param>
/// <param name="databaseFilePath"></param>
/// <param name="fivePrimeTerminus"></param>
Expand All @@ -33,12 +33,12 @@ public RNA(string sequence, IHasChemicalFormula? fivePrimeTerm = null, IHasChemi
/// <param name="isDecoy"></param>
/// <param name="geneNames"></param>
/// <param name="databaseAdditionalFields"></param>
public RNA(string sequence, string name, string identifier, string organism, string databaseFilePath,
public RNA(string sequence, string name, string accession, string organism, string databaseFilePath,
IHasChemicalFormula? fivePrimeTerminus = null, IHasChemicalFormula? threePrimeTerminus = null,
IDictionary<int, List<Modification>>? oneBasedPossibleModifications = null,
bool isContaminant = false, bool isDecoy = false, List<Tuple<string, string>> geneNames = null,
Dictionary<string, string>? databaseAdditionalFields = null)
: base(sequence, name, identifier, organism, databaseFilePath, fivePrimeTerminus, threePrimeTerminus,
: base(sequence, name, accession, organism, databaseFilePath, fivePrimeTerminus, threePrimeTerminus,
oneBasedPossibleModifications, isContaminant, isDecoy, geneNames, databaseAdditionalFields)
{

Expand Down
1 change: 1 addition & 0 deletions mzLib/mzLib.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Modomics/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nucleolytic/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Oligo/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Oligos/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prsm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Regexes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Toppic/@EntryIndexedValue">True</s:Boolean>
Expand Down

0 comments on commit 3fb1b33

Please sign in to comment.