CoolFace
Apppublic

ml-jku/tox21_xgboost_classifier

sourceHugging Facecc-by-nc-4.0updated 8mo agoView on Hugging Face
0likes
utils.py453 linesDownload Raw Back to src
1## These MolStandardizer classes are due to Paolo Tosco2## It was taken from the FS-Mol github3## (https://github.com/microsoft/FS-Mol/blob/main/fs_mol/preprocessing/utils/4##  standardizer.py)5## They ensure that a sequence of standardization operations are applied6## https://gist.github.com/ptosco/7e6b9ab9cc3e44ba0919060beaed198e7 8import os9 10from rdkit import Chem11from rdkit.Chem.MolStandardize import rdMolStandardize12 13HF_TOKEN = os.environ.get("HF_TOKEN")14TOX_SMARTS_PATH = "data/tox_smarts.json"15 16TASKS = [17    "NR-AR",18    "NR-AR-LBD",19    "NR-AhR",20    "NR-Aromatase",21    "NR-ER",22    "NR-ER-LBD",23    "NR-PPAR-gamma",24    "SR-ARE",25    "SR-ATAD5",26    "SR-HSE",27    "SR-MMP",28    "SR-p53",29]30 31USED_200_DESCR = [32    0,33    1,34    2,35    3,36    4,37    5,38    6,39    7,40    8,41    9,42    10,43    11,44    12,45    13,46    14,47    15,48    16,49    25,50    26,51    27,52    28,53    29,54    30,55    31,56    32,57    33,58    34,59    35,60    36,61    37,62    38,63    39,64    40,65    41,66    42,67    43,68    44,69    45,70    46,71    47,72    48,73    49,74    50,75    51,76    52,77    53,78    54,79    55,80    56,81    57,82    58,83    59,84    60,85    61,86    62,87    63,88    64,89    65,90    66,91    67,92    68,93    69,94    70,95    71,96    72,97    73,98    74,99    75,100    76,101    77,102    78,103    79,104    80,105    81,106    82,107    83,108    84,109    85,110    86,111    87,112    88,113    89,114    90,115    91,116    92,117    93,118    94,119    95,120    96,121    97,122    98,123    99,124    100,125    101,126    102,127    103,128    104,129    105,130    106,131    107,132    108,133    109,134    110,135    111,136    112,137    113,138    114,139    115,140    116,141    117,142    118,143    119,144    120,145    121,146    122,147    123,148    124,149    125,150    126,151    127,152    128,153    129,154    130,155    131,156    132,157    133,158    134,159    135,160    136,161    137,162    138,163    139,164    140,165    141,166    142,167    143,168    144,169    145,170    146,171    147,172    148,173    149,174    150,175    151,176    152,177    153,178    154,179    155,180    156,181    157,182    158,183    159,184    160,185    161,186    162,187    163,188    164,189    165,190    166,191    167,192    168,193    169,194    170,195    171,196    172,197    173,198    174,199    175,200    176,201    177,202    178,203    179,204    180,205    181,206    182,207    183,208    184,209    185,210    186,211    187,212    188,213    189,214    190,215    191,216    192,217    193,218    194,219    195,220    196,221    197,222    198,223    199,224    200,225    201,226    202,227    203,228    204,229    205,230    206,231    207,232]233 234 235class Standardizer:236    """237    Simple wrapper class around rdkit Standardizer.238    """239 240    DEFAULT_CANON_TAUT = False241    DEFAULT_METAL_DISCONNECT = False242    MAX_TAUTOMERS = 100243    MAX_TRANSFORMS = 100244    MAX_RESTARTS = 200245    PREFER_ORGANIC = True246 247    def __init__(248        self,249        metal_disconnect=None,250        canon_taut=None,251    ):252        """253        Constructor.254        All parameters are optional.255        :param metal_disconnect:    if True, metallorganic complexes are256                                    disconnected257        :param canon_taut:          if True, molecules are converted to their258                                    canonical tautomer259        """260        super().__init__()261        if metal_disconnect is None:262            metal_disconnect = self.DEFAULT_METAL_DISCONNECT263        if canon_taut is None:264            canon_taut = self.DEFAULT_CANON_TAUT265        self._canon_taut = canon_taut266        self._metal_disconnect = metal_disconnect267        self._taut_enumerator = None268        self._uncharger = None269        self._lfrag_chooser = None270        self._metal_disconnector = None271        self._normalizer = None272        self._reionizer = None273        self._params = None274 275    @property276    def params(self):277        """Return the MolStandardize CleanupParameters."""278        if self._params is None:279            self._params = rdMolStandardize.CleanupParameters()280            self._params.maxTautomers = self.MAX_TAUTOMERS281            self._params.maxTransforms = self.MAX_TRANSFORMS282            self._params.maxRestarts = self.MAX_RESTARTS283            self._params.preferOrganic = self.PREFER_ORGANIC284            self._params.tautomerRemoveSp3Stereo = False285        return self._params286 287    @property288    def canon_taut(self):289        """Return whether tautomer canonicalization will be done."""290        return self._canon_taut291 292    @property293    def metal_disconnect(self):294        """Return whether metallorganic complexes will be disconnected."""295        return self._metal_disconnect296 297    @property298    def taut_enumerator(self):299        """Return the TautomerEnumerator object."""300        if self._taut_enumerator is None:301            self._taut_enumerator = rdMolStandardize.TautomerEnumerator(self.params)302        return self._taut_enumerator303 304    @property305    def uncharger(self):306        """Return the Uncharger object."""307        if self._uncharger is None:308            self._uncharger = rdMolStandardize.Uncharger()309        return self._uncharger310 311    @property312    def lfrag_chooser(self):313        """Return the LargestFragmentChooser object."""314        if self._lfrag_chooser is None:315            self._lfrag_chooser = rdMolStandardize.LargestFragmentChooser(316                self.params.preferOrganic317            )318        return self._lfrag_chooser319 320    @property321    def metal_disconnector(self):322        """Return the MetalDisconnector object."""323        if self._metal_disconnector is None:324            self._metal_disconnector = rdMolStandardize.MetalDisconnector()325        return self._metal_disconnector326 327    @property328    def normalizer(self):329        """Return the Normalizer object."""330        if self._normalizer is None:331            self._normalizer = rdMolStandardize.Normalizer(332                self.params.normalizationsFile, self.params.maxRestarts333            )334        return self._normalizer335 336    @property337    def reionizer(self):338        """Return the Reionizer object."""339        if self._reionizer is None:340            self._reionizer = rdMolStandardize.Reionizer(self.params.acidbaseFile)341        return self._reionizer342 343    def charge_parent(self, mol_in):344        """Sequentially apply a series of MolStandardize operations:345        * MetalDisconnector346        * Normalizer347        * Reionizer348        * LargestFragmentChooser349        * Uncharger350        The net result is that a desalted, normalized, neutral351        molecule with implicit Hs is returned.352        """353        params = Chem.RemoveHsParameters()354        params.removeAndTrackIsotopes = True355        mol_in = Chem.RemoveHs(mol_in, params, sanitize=False)356        if self._metal_disconnect:357            mol_in = self.metal_disconnector.Disconnect(mol_in)358        normalized = self.normalizer.normalize(mol_in)359        Chem.SanitizeMol(normalized)360        normalized = self.reionizer.reionize(normalized)361        Chem.AssignStereochemistry(normalized)362        normalized = self.lfrag_chooser.choose(normalized)363        normalized = self.uncharger.uncharge(normalized)364        # need this to reassess aromaticity on things like365        # cyclopentadienyl, tropylium, azolium, etc.366        Chem.SanitizeMol(normalized)367        return Chem.RemoveHs(Chem.AddHs(normalized))368 369    def standardize_mol(self, mol_in):370        """371        Standardize a single molecule.372        :param mol_in:  a Chem.Mol373        :return:        * (standardized Chem.Mol, n_taut) tuple374                          if success. n_taut will be negative if375                          tautomer enumeration was aborted due376                          to reaching a limit377                        * (None, error_msg) if failure378        This calls self.charge_parent() and, if self._canon_taut379        is True, runs tautomer canonicalization.380        """381        n_tautomers = 0382        if isinstance(mol_in, Chem.Mol):383            name = None384            try:385                name = mol_in.GetProp("_Name")386            except KeyError:387                pass388            if not name:389                name = "NONAME"390        else:391            error = f"Expected SMILES or Chem.Mol as input, got {str(type(mol_in))}"392            return None, error393        try:394            mol_out = self.charge_parent(mol_in)395        except Exception as e:396            error = f"charge_parent FAILED: {str(e).strip()}"397            return None, error398        if self._canon_taut:399            try:400                res = self.taut_enumerator.Enumerate(mol_out, False)401            except TypeError:402                # we are still on the pre-2021 RDKit API403                res = self.taut_enumerator.Enumerate(mol_out)404            except Exception as e:405                # something else went wrong406                error = f"canon_taut FAILED: {str(e).strip()}"407                return None, error408            n_tautomers = len(res)409            if hasattr(res, "status"):410                completed = (411                    res.status == rdMolStandardize.TautomerEnumeratorStatus.Completed412                )413            else:414                # we are still on the pre-2021 RDKit API415                completed = len(res) < 1000416            if not completed:417                n_tautomers = -n_tautomers418            try:419                mol_out = self.taut_enumerator.PickCanonical(res)420            except AttributeError:421                # we are still on the pre-2021 RDKit API422                mol_out = max(423                    [(self.taut_enumerator.ScoreTautomer(m), m) for m in res]424                )[1]425            except Exception as e:426                # something else went wrong427                error = f"canon_taut FAILED: {str(e).strip()}"428                return None, error429        mol_out.SetProp("_Name", name)430        return mol_out, n_tautomers431 432 433def create_dir(path, is_file=False):434    """Creates the parent directories if a path to a file is given, else create the given directory"""435 436    to_create = os.path.dirname(path) if is_file else path437    if not os.path.exists(to_create):438        os.makedirs(to_create)439 440 441def normalize_config(config: dict):442    """Normalizes a json config recursively by applying a mapping"""443    mapping = {"none": None, "true": True, "false": False}444    new_config = {}445    for key, val in config.items():446        if isinstance(val, dict):447            new_config[key] = normalize_config(val)448        elif isinstance(val, (int, float, str)) and val in mapping:449            new_config[key] = mapping[val]450        else:451            new_config[key] = val452    return new_config453