Skip to content
Snippets Groups Projects
Commit 8984d76f authored by Studer Gabriel's avatar Studer Gabriel
Browse files

SCHWED-6298: QS-score for two monomers is defined as 1.0

They have the exact same quaternary structure in the end... This definition
has been generalized to: if there are no contacts observed, QS-score is 1.0
if the scored structures have the same stoichiometry, 0.0 otherwise.
This becomes a bit more abstract for higher order complexes but in principle
they still have the exact same quaternary structure if they match in
stoichiometry but have no single contact.
parent 0665a173
No related branches found
No related tags found
No related merge requests found
......@@ -213,11 +213,12 @@ class QSScorerResult:
from `Xu et al. 2009 <https://dx.doi.org/10.1016%2Fj.jmb.2008.06.002>`_.
"""
def __init__(self, weighted_scores, weight_sum, weight_extra_mapped,
weight_extra_all):
weight_extra_all, complete_mapping):
self._weighted_scores = weighted_scores
self._weight_sum = weight_sum
self._weight_extra_mapped = weight_extra_mapped
self._weight_extra_all = weight_extra_all
self._complete_mapping = complete_mapping
@property
def weighted_scores(self):
......@@ -251,11 +252,30 @@ class QSScorerResult:
"""
return self._weight_extra_all
@property
def complete_mapping(self):
""" Whether the underlying mapping of the scored assemblies is complete
In other words: If they have the same stoichiometry. This is relevant
for :attr:`~QS_best` and :attr:`~QS_global` in case of no contacts in
any of the scored entities.
:type: :class:`bool`
"""
return self._complete_mapping
@property
def QS_best(self):
""" QS_best - the actual score as described in formula section above
Returns None if there are no contacts in the compared structures
If there are no contacts observed in any of the scored entities this
score is 1.0 if we're comparing structures with
:attr:`~complete_mapping`, 0.0 otherwise. In the example of two
monomers, no contacts can be observed but they exactly match in terms
of quaternary structure. Thus a perfect score. In terms of higher order
structure that becomes a bit more abstract but in principle they still
have the exact same quaternary structure if they match in stoichiometry
but have no single contact.
:type: :class:`float`
"""
......@@ -263,14 +283,23 @@ class QSScorerResult:
denominator = self.weight_sum + self.weight_extra_mapped
if denominator != 0.0:
return nominator/denominator
elif self.complete_mapping:
return 1.0
else:
return None
return 0.0
@property
def QS_global(self):
""" QS_global - the actual score as described in formula section above
Returns None if there are no contacts in the compared structures
If there are no contacts observed in any of the scored entities this
score is 1.0 if we're comparing structures with
:attr:`~complete_mapping`, 0.0 otherwise. In the example of two
monomers, no contacts can be observed but they exactly match in terms
of quaternary structure. Thus a perfect score. In terms of higher order
structure that becomes a bit more abstract but in principle they still
have the exact same quaternary structure if they match in stoichiometry
but have no single contact.
:type: :class:`float`
"""
......@@ -278,8 +307,10 @@ class QSScorerResult:
denominator = self.weight_sum + self.weight_extra_all
if denominator != 0.0:
return nominator/denominator
elif self.complete_mapping:
return 1.0
else:
return None
return 0.0
class QSScorer:
......@@ -457,6 +488,11 @@ class QSScorer:
This only works for interfaces that are computed in :func:`Score`, i.e.
interfaces for which the alignments are set up correctly.
As all specified chains must be present, the mapping is considered
complete which affects
:attr:`QSScorerResult.QS_global`/:attr:`QSScorerResult.QS_best` in
edge cases of no observed contacts.
:param trg_ch1: Name of first interface chain in target
:type trg_ch1: :class:`str`
:param trg_ch2: Name of second interface chain in target
......@@ -484,7 +520,10 @@ class QSScorer:
trg_int = (trg_ch1, trg_ch2)
mdl_int = (mdl_ch1, mdl_ch2)
a, b, c, d = self._MappedInterfaceScores(trg_int, mdl_int)
return QSScorerResult(a, b, c, d)
# complete_mapping is True by definition, as the requested chain pairs
# are both present
return QSScorerResult(a, b, c, d, True)
def FromFlatMapping(self, flat_mapping):
""" Same as :func:`Score` but with flat mapping
......@@ -530,8 +569,16 @@ class QSScorer:
else:
weight_extra_all += self._InterfacePenalty2(int2)
trg_chains = sorted(self.qsent1.chain_names) # should be sorted already
mdl_chains = sorted(self.qsent2.chain_names) # should be sorted already
mapped_trg_chains = sorted(flat_mapping.keys())
mapped_mdl_chains = sorted(flat_mapping.values())
trg_complete = trg_chains == mapped_trg_chains
mdl_complete = mdl_chains == mapped_mdl_chains
complete_mapping = trg_complete and mdl_complete
return QSScorerResult(weighted_scores, weight_sum, weight_extra_mapped,
weight_extra_all)
weight_extra_all, complete_mapping)
def _MappedInterfaceScores(self, int1, int2):
key_one = (int1, int2)
......@@ -704,4 +751,4 @@ class QSScorer:
return penalty
# specify public interface
__all__ = ('QSEntity', 'QSScorer')
__all__ = ('QSEntity', 'QSScorer', 'QSScorerResult')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment