diff --git a/modules/mol/alg/pymod/chain_mapping.py b/modules/mol/alg/pymod/chain_mapping.py index 29acb66541deedd75b02fa3d4bb07bb65555dda4..a738493f45175560dc10eb11a3ed2ba5d9090808 100644 --- a/modules/mol/alg/pymod/chain_mapping.py +++ b/modules/mol/alg/pymod/chain_mapping.py @@ -26,12 +26,14 @@ class MappingResult: Constructor is directly called within the functions, no need to construct such objects yourself. """ - def __init__(self, target, model, chem_groups, mapping, alns): + def __init__(self, target, model, chem_groups, mapping, alns, + opt_score=None): self._target = target self._model = model self._chem_groups = chem_groups self._mapping = mapping self._alns = alns + self._opt_score = opt_score @property def target(self): @@ -89,6 +91,18 @@ class MappingResult: """ return self._alns + @property + def opt_score(self): + """ Placeholder property without any guarantee of being set + + Different scores get optimized in the various chain mapping algorithms. + Some of them may set their final optimal score in that property. + Consult the documentation of the respective chain mapping algorithm + for more information. Won't be in the return dict of + :func:`JSONSummary`. + """ + return self._opt_score + def GetFlatMapping(self, mdl_as_key=False): """ Returns flat mapping as :class:`dict` for all mapable chains @@ -742,6 +756,9 @@ class ChainMapper: scoring ones are extend by *block_seed_size* chains and the best scoring one is exhaustively extended. + Sets :attr:`MappingResult.opt_score` in case of no trivial one-to-one + mapping. + :param model: Model to map :type model: :class:`ost.mol.EntityView`/:class:`ost.mol.EntityHandle` :param inclusion_radius: Inclusion radius for lDDT @@ -809,11 +826,13 @@ class ChainMapper: alns) mapping = None + opt_lddt = None if strategy == "naive": - mapping = _lDDTNaive(self.target, mdl, inclusion_radius, thresholds, - self.chem_groups, chem_mapping, ref_mdl_alns, - self.n_max_naive) + mapping, opt_lddt = _lDDTNaive(self.target, mdl, inclusion_radius, + thresholds, self.chem_groups, + chem_mapping, ref_mdl_alns, + self.n_max_naive) else: # its one of the greedy strategies - setup greedy searcher the_greed = _lDDTGreedySearcher(self.target, mdl, self.chem_groups, @@ -828,6 +847,8 @@ class ChainMapper: elif strategy == "greedy_block": mapping = _lDDTGreedyBlock(the_greed, block_seed_size, block_blocks_per_chem_group) + # cached => lDDT computation is fast here + opt_lddt = the_greed.lDDT(self.chem_groups, mapping) alns = dict() for ref_group, mdl_group in zip(self.chem_groups, mapping): @@ -839,7 +860,7 @@ class ChainMapper: alns[(ref_ch, mdl_ch)] = aln return MappingResult(self.target, mdl, self.chem_groups, mapping, - alns) + alns, opt_score = opt_lddt) def GetQSScoreMapping(self, model, contact_d = 12.0, strategy = "naive", @@ -876,6 +897,9 @@ class ChainMapper: scoring ones are extend by *block_seed_size* chains and the block with with best QS score is exhaustively extended. + Sets :attr:`MappingResult.opt_score` in case of no trivial one-to-one + mapping. + :param model: Model to map :type model: :class:`ost.mol.EntityView`/:class:`ost.mol.EntityHandle` :param contact_d: Max distance between two residues to be considered as @@ -917,18 +941,21 @@ class ChainMapper: alns[(ref_ch, mdl_ch)] = aln return MappingResult(self.target, mdl, self.chem_groups, one_to_one, alns) + mapping = None + opt_qsscore = None if strategy == "naive": - mapping = _QSScoreNaive(self.target, mdl, self.chem_groups, - chem_mapping, ref_mdl_alns, contact_d, - self.n_max_naive) + mapping, opt_qsscore = _QSScoreNaive(self.target, mdl, + self.chem_groups, + chem_mapping, ref_mdl_alns, + contact_d, self.n_max_naive) else: # its one of the greedy strategies - setup greedy searcher - - the_greed = _QSScoreGreedySearcher(self.target, mdl, self.chem_groups, - chem_mapping, ref_mdl_alns, - contact_d = contact_d, - steep_opt_rate=steep_opt_rate) + the_greed = _QSScoreGreedySearcher(self.target, mdl, + self.chem_groups, + chem_mapping, ref_mdl_alns, + contact_d = contact_d, + steep_opt_rate=steep_opt_rate) if strategy == "greedy_fast": mapping = _QSScoreGreedyFast(the_greed) elif strategy == "greedy_full": @@ -936,6 +963,9 @@ class ChainMapper: elif strategy == "greedy_block": mapping = _QSScoreGreedyBlock(the_greed, block_seed_size, block_blocks_per_chem_group) + # cached => QSScore computation is fast here + opt_qsscore = the_greed.Score(mapping, check=False) + alns = dict() for ref_group, mdl_group in zip(self.chem_groups, mapping): @@ -947,7 +977,7 @@ class ChainMapper: alns[(ref_ch, mdl_ch)] = aln return MappingResult(self.target, mdl, self.chem_groups, mapping, - alns) + alns, opt_score = opt_qsscore) def GetRigidMapping(self, model, strategy = "greedy_single_gdtts", single_chain_gdtts_thresh=0.4, subsampling=None, @@ -2207,7 +2237,7 @@ def _lDDTNaive(trg, mdl, inclusion_radius, thresholds, chem_groups, best_mapping = mapping best_lddt = lDDT - return best_mapping + return (best_mapping, best_lddt) def _lDDTGreedyFast(the_greed): @@ -2630,7 +2660,7 @@ def _QSScoreNaive(trg, mdl, chem_groups, chem_mapping, ref_mdl_alns, contact_d, if score_result.QS_global > best_score: best_mapping = mapping best_score = score_result.QS_global - return best_mapping + return (best_mapping, best_score) def _QSScoreGreedyFast(the_greed):