Source code for schrodinger.application.phase.hypothesis

"""
Module for reading and writing Phase hypotheses.
"""

from collections import OrderedDict

from schrodinger.application.phase import constants
from schrodinger.infra import mm
from schrodinger.infra import phase
from schrodinger.structure import Structure
from schrodinger.structure import StructureReader
from schrodinger.structure import _StructureProperty


class _PhaseProperty(_StructureProperty):
    """
    Return a MMCT-style property container.

    Properties are read like a MMCT structure, but they are written directly
    to the parent PhaseHypothesis.
    """

    def __init__(self, hypothesis):
        ct = hypothesis.getHypoCt()
        self.setitem = hypothesis.addProp
        super().__init__(Structure(ct), read_only=False)

    def __setitem__(self, name, value):
        return self.setitem(name, value)


[docs]class PhaseHypothesis(phase.PhpHypoAdaptor):
[docs] def __init__(self, *args): """ Initializes a Phase hypothesis from `phase.PhpHypoAdaptor` """ # Create PhaseHypothesis from set of CTs super().__init__(*args) # TODO: Remove once PHASE-1873 has been implemented # Enforce that all hypotheses have Tol/Rules/Mask/Rad attributes self.buildAllAttr(True)
@property def title(self): """ Returns the hypothesis ID as the title :return: hypothesis ID title :rtype: str """ return self.getHypoID() @property def property(self): """ Make hypothesis property available as in `structure.Structure`, drawn from the hypothesis ct. When adding a value into the property dictionary, the `phase.PhpHypoAdaptor` addProp interface is used. """ return _PhaseProperty(self) ct = self.getHypoCt() return _PhaseProperty(Structure(ct), self.addProp)
[docs] def getSiteNumber(self, site): """ Determines site number for the given feature site by comparing its name with names of all the current 'reference' sites. :param site: feature site object :type site: `phase.PhpSite` :return: site number of the current feature in the hypothesis :rtype: int or None """ site_name = site.getDisplayName() all_sites = self.getRefSites() site_names = [site.getDisplayName() for site in all_sites] return site_names.index(site_name)
[docs] def addFeatureTypeEquivalencies(self, equivalencies): """ Applies feature presets to a given hypothesis. :param equiv_pairs: list of equivalency feauture types :type equiv_pairs: list """ if self.hasRules(): hypo_rules = self.getRules() permitted = list(hypo_rules.getPermittedFeatures()) prohibited = list(hypo_rules.getProhibitedFeatures()) else: # create default rules, where the current site is the only # permitted feature. permitted = [site.getSiteTypeChar() for site in self.getHypoSites()] prohibited = ['' for site in self.getHypoSites()] # Update 'permitted' rules for each site. for preset in equivalencies: # each preset is a two character string, where each character is # a one letter feature type. Here we create a list of feature # types in the current preset. for site_index, site in enumerate(self.getHypoSites()): # Find sites, whose feature type matches a feature in the # current preset and add rule. if site.getSiteType() in preset: permitted[site_index] = permitted[site_index] + ''.join( [x for x in preset if x not in permitted[site_index]]) # Update hypothesis feature rules new_rules = phase.PhpFeatureRules() for site_index, site in enumerate(self.getHypoSites()): if self.hasRules(): site_num = hypo_rules.getSiteNumber(site_index) else: site_num = self.getSiteNumber(site) new_rules.addRule(site_num, permitted[site_index], prohibited[site_index]) self.addRules(new_rules)
[docs] def write(self, filename): """ Writes the hypothesis to disk in the single file `*.phypo` format :param filename: hypothesis filename to write to :type: str """ if not filename.endswith(phase.PHASE_HYPO_FILE_EXT): filename += phase.PHASE_HYPO_FILE_EXT overwrite = True self.save(filename, overwrite)
[docs] def visibleXvol(self): """ Returns if the excluded volume visibility property is enabled. """ if not self.hasXvol(): return False # Excluded volumes should be shown by default return self.property.get(constants.HYPO_XVOL_VISIBLE, True)
[docs] def visibleRefCt(self): """ Returns if the reference ligand visibility property is enabled. """ if not self.hasRefCt(): return False # Reference ligands should be hidden by default return self.property.get(constants.HYPO_REFCT_VISIBLE, False)
[docs] def visibleTol(self): """ Returns if the tolerances visibility property is enabled. """ # Show tolerance spheres when hypothesis is shown for the # very first time. return self.property.get(constants.HYPO_TOL_VISIBLE, True)
[docs] def visibleFeatureLabels(self): """ Returns if the feature labels should be shown. """ return self.property.get(constants.FEATURE_LABELS_VISIBLE, True)
[docs] def visiblePropLabels(self): """ Returns if the property labels should be shown. If feature labels are not shown property labels will be always be hidden. """ return self.property.get(constants.PROP_LABELS_VISIBLE, False)
[docs] def manageXvolIsOpen(self): """ Returns True if manage excluded volumes panel for this hypothesis is open. """ return self.property.get(constants.MANAGE_XVOL_OPEN, False)
# ============================================================================= # Super classes to differentiate overloaded constructors # =============================================================================
[docs]class PhaseHypothesisStandard(PhaseHypothesis): """ Phase hypothesis created from standard sites found in the given reference ligand. Phase default feature defintions are used if not supplied. """
[docs] def __init__(self, hypo_ID, standard_sites, reference_ligand, \ feature_defintions=None): """ :param hypo_ID: hypothesis ID :type hypo_ID: str :param standard_sites: sites found in the reference structure :type standard_sites: list of `phase.PhpSite` :param reference_ligand: hypothesis reference ligand which can be either structure (preferred) or structure handle :type reference_ligand: structure.Structure or int :param feature_defintions: feature definitions used in the hypothesis :type feature_defintions: list of `phase.PhpFeatureDefinition` """ if not feature_defintions: feature_defintions = phase.PhpProject().getDefaultFeatureDef() super().__init__(hypo_ID, standard_sites, feature_defintions, reference_ligand)
[docs]class PhaseHypothesisStatic(PhaseHypothesis): """ Phase hypothesis created from sites which will all be converted into static fragment sites. Phase default feature defintions are used if not supplied. """
[docs] def __init__(self, hypo_ID, static_sites, feature_defintions=None): """ :param hypo_ID: hypothesis ID :type hypo_ID: str :param static_sites: sites which will be converted to static fragments :type static_sites: list of `phase.PhpSite` :param feature_defintions: feature definitions used in the hypothesis :type feature_defintions: list of `phase.PhpFeatureDefinition` """ if not feature_defintions: feature_defintions = phase.PhpProject().getDefaultFeatureDef() super().__init__(hypo_ID, [], static_sites, feature_defintions, mm.MMCT_INVALID_CT)
[docs]class PhaseHypothesisHybrid(PhaseHypothesis): """ Phase hypothesis created from both sites which will all be converted into static fragment sites, as well as standard sites found in the given reference ligand. Phase default feature defintions are used if not supplied. """
[docs] def __init__(self, hypo_ID, static_sites, standard_sites, \ reference_ligand, feature_defintions=None): """ :param hypo_ID: hypothesis ID :type hypo_ID: str :param static_sites: sites which will be converted to static fragments :type static_sites: list of `phase.PhpSite` :param standard_sites: sites found in the reference structure :type standard_sites: list of `phase.PhpSite` :param reference_ligand: hypothesis reference ligand which can be either structure (preferred) or structure handle :type reference_ligand: structure.Structure or int :param feature_defintions: feature definitions used in the hypothesis :type feature_defintions: list of `phase.PhpFeatureDefinition` """ if not feature_defintions: feature_defintions = phase.PhpProject().getDefaultFeatureDef() super().__init__(hypo_ID, standard_sites, static_sites, feature_defintions, reference_ligand)
[docs]class PhaseHypothesisFromLigand(PhaseHypothesis): """ Phase hypothesis created from all mappable features in the given ligand. Phase default feature defintions are used if not supplied. """
[docs] def __init__(self, ligand, feature_definitions=None): """ :param ligand: reference ligand, from which to extract all features which can be either structure (preferred) or structure handle :type ligand: structure.Structure or int :param feature_definitions: feature definitions used in the hypothesis :type feature_definitions: list of phase.PhpFeatureDefinition """ project = phase.PhpProject() if not feature_definitions: feature_definitions = project.getDefaultFeatureDef() hypo_ID = ligand.title standard_sites = project.getMolSites(ligand, feature_definitions) super(PhaseHypothesisFromLigand, self).__init__(hypo_ID, standard_sites, feature_definitions, ligand)
# ============================================================================= # PhaseHypothesis utilities # =============================================================================
[docs]def sorted_hypo_structures(hypothesis_sts): """ Sorts a list of structures for PhaseHypothesis - Phase hypothesis CT - Phase hypothesis reference ligand - Any additional Phase hypothesis ligands :param hypothesis_sts: list of structures :type hypothesis_sts: list of `Structure` :return: hypothesis, reference ligand, additional hypothesis ligands :rtype: `Structure`, `Structure`, list of `Structure` """ hypo_st = mm.MMCT_INVALID_CT ref_st = mm.MMCT_INVALID_CT add_sts = [] for st in hypothesis_sts: hypo_role = st.property.get(phase.PHASE_HYPO_ROLE, None) if hypo_role == phase.ROLE_HYPO: hypo_st = st # PANEL-8386: Left for backward compatibility elif hypo_role == phase.ROLE_REF: ref_st = st elif hypo_role in constants.ADDITIONAL_ROLE: add_sts.append(st) # PANEL-8386: Extract reference ligand from hypo_st hypo_obj = PhaseHypothesis(hypo_st) if hypo_obj.hasRefCt(): ref_st = Structure(hypo_obj.getRefCt()) return hypo_st, ref_st, add_sts
[docs]def extract_hypotheses(filename): """ Extracts all hypotheses from Phase hypothesis file containing multiple hypotheses :param filename: `*_phypo.mae.gz` file containing Phase hypotheses :type filename: str :return: list of Phase hypotheses :rtype: list of `PhaseHypothesis` """ # Collect structures by hypothesis ID hypo_structures = OrderedDict() for st in StructureReader(filename): hypo_ID = st.property[phase.PHASE_HYPO_ID] if hypo_ID not in hypo_structures: hypo_structures[hypo_ID] = [] hypo_structures[hypo_ID].append(st) # Sort structures and create PhaseHypothesis objects hypotheses = [] for sts in hypo_structures.values(): hypo_st, ref_st, add_sts = sorted_hypo_structures(sts) hypotheses.append(PhaseHypothesis(hypo_st, ref_st, add_sts)) return hypotheses
[docs]def extract_structures(hypothesis, group=None): """ Generator extracting all structures from a PhaseHypothesis objects. If a group name is specified, hypothesis group properties will be added. :param hypothesis: Phase hypothesis :type hypothesis: `PhaseHypothesis` :param group: name to group all hypothesis structures under :type group: str :return: next structure extracted from input hypothesis :rtype: structure.Structure """ # If grouping, adjust the hypothesis subgroup if group: subgroup = group + mm.M2IO_SUBGROUP_SEPARATOR + hypothesis.getHypoID() hypothesis.addProp(mm.M2IO_DATA_SUBGROUPID, subgroup) # Collapse each hypothesis by default hypothesis.addProp(mm.M2IO_DATA_SUBGROUP_COLLAPSED, True) hypo_st = Structure(hypothesis.getHypoCt()) yield hypo_st # Append any alignments to hypothesis group for ct in hypothesis.getAddCts(): st = Structure(ct) if group: subgroup = st.property[mm.M2IO_DATA_SUBGROUPID] new_subgroup = group + mm.M2IO_SUBGROUP_SEPARATOR + subgroup st.property[mm.M2IO_DATA_SUBGROUPID] = new_subgroup yield st