From d856b2c043ac1908f6ec4dfadba8e51b7ef93c73 Mon Sep 17 00:00:00 2001 From: sfluegel Date: Mon, 3 Aug 2026 09:51:19 +0200 Subject: [PATCH 1/5] use sanitized molecule --- chebai_graph/preprocessing/reader/reader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chebai_graph/preprocessing/reader/reader.py b/chebai_graph/preprocessing/reader/reader.py index d673ccf..ef6e282 100644 --- a/chebai_graph/preprocessing/reader/reader.py +++ b/chebai_graph/preprocessing/reader/reader.py @@ -60,7 +60,7 @@ def _smiles_to_mol(self, smiles: str) -> Chem.rdchem.Mol | None: self.failed_counter += 1 else: try: - _sanitize_molecule(mol) + mol = _sanitize_molecule(mol) except Exception as e: print(f"Rdkit failed at sanitizing {smiles}, \n Error: {e}") self.failed_counter += 1 @@ -213,7 +213,7 @@ def _smiles_to_mol(self, smiles: str) -> Chem.rdchem.Mol | None: print(f"RDKit failed to at parsing {smiles} (returned None)") else: try: - _sanitize_molecule(mol) + mol = _sanitize_molecule(mol) except Exception as e: print(f"Rdkit failed at sanitizing {smiles}, \n Error: {e}") return mol From 31e6613a26623414d40a856ea9aa104d33fb193b Mon Sep 17 00:00:00 2001 From: sfluegel Date: Mon, 3 Aug 2026 10:28:01 +0200 Subject: [PATCH 2/5] remove broken assertion --- chebai_graph/preprocessing/reader/reader.py | 1 - 1 file changed, 1 deletion(-) diff --git a/chebai_graph/preprocessing/reader/reader.py b/chebai_graph/preprocessing/reader/reader.py index ef6e282..89ffece 100644 --- a/chebai_graph/preprocessing/reader/reader.py +++ b/chebai_graph/preprocessing/reader/reader.py @@ -166,7 +166,6 @@ def _read_data(self, raw_data: str | Chem.Mol) -> GeomData | None: ) except ValueError: return None - assert isinstance(mol, nx.Graph) d: dict[int, int] = {} de: dict[tuple[int, int], int] = {} for node in mol.nodes: From e3f40bca6e42968ce3295549d860a48ae9c354fc Mon Sep 17 00:00:00 2001 From: sfluegel Date: Mon, 3 Aug 2026 11:05:24 +0200 Subject: [PATCH 3/5] use smiles_to_mol function from chebi_utils --- chebai_graph/preprocessing/reader/reader.py | 44 ++++----------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/chebai_graph/preprocessing/reader/reader.py b/chebai_graph/preprocessing/reader/reader.py index 89ffece..7415ec1 100644 --- a/chebai_graph/preprocessing/reader/reader.py +++ b/chebai_graph/preprocessing/reader/reader.py @@ -1,7 +1,7 @@ import os import chebai.preprocessing.reader as dr -from chebi_utils.sdf_extractor import _sanitize_molecule +from chebi_utils.read_molecule import smiles_or_inchi_to_mol import networkx as nx import rdkit.Chem as Chem import torch @@ -29,7 +29,7 @@ def __init__( """ super().__init__(*args, **kwargs) self.failed_counter = 0 - self.mol_object_buffer: dict[str, Chem.rdchem.Mol | None] = {} + self.mol_object_buffer: dict[str, Chem.Mol | None] = {} @classmethod def name(cls) -> str: @@ -41,7 +41,7 @@ def name(cls) -> str: """ return "graph_properties" - def _smiles_to_mol(self, smiles: str) -> Chem.rdchem.Mol | None: + def _smiles_to_mol(self, smiles: str) -> Chem.Mol | None: """ Load SMILES string into an RDKit molecule object and cache it. @@ -49,21 +49,12 @@ def _smiles_to_mol(self, smiles: str) -> Chem.rdchem.Mol | None: smiles (str): The SMILES string to parse. Returns: - Chem.rdchem.Mol | None: Parsed molecule object or None if parsing failed. + Chem.Mol | None: Parsed molecule object or None if parsing failed. """ if smiles in self.mol_object_buffer: return self.mol_object_buffer[smiles] - mol = Chem.MolFromSmiles(smiles, sanitize=False) - if mol is None: - print(f"RDKit failed to at parsing {smiles} (returned None)") - self.failed_counter += 1 - else: - try: - mol = _sanitize_molecule(mol) - except Exception as e: - print(f"Rdkit failed at sanitizing {smiles}, \n Error: {e}") - self.failed_counter += 1 + mol = smiles_or_inchi_to_mol(smiles) self.mol_object_buffer[smiles] = mol return mol @@ -162,7 +153,9 @@ def _read_data(self, raw_data: str | Chem.Mol) -> GeomData | None: # raw_data is a SMILES string try: mol = ( - self._smiles_to_mol(raw_data) if isinstance(raw_data, str) else raw_data + smiles_or_inchi_to_mol(raw_data) + if isinstance(raw_data, str) + else raw_data ) except ValueError: return None @@ -196,27 +189,6 @@ def _read_data(self, raw_data: str | Chem.Mol) -> GeomData | None: data = from_networkx(mol) return data - def _smiles_to_mol(self, smiles: str) -> Chem.rdchem.Mol | None: - """ - Load SMILES string into an RDKit molecule object. - - Args: - smiles (str): The SMILES string to parse. - - Returns: - Chem.rdchem.Mol | None: Parsed molecule object or None if parsing failed. - """ - - mol = Chem.MolFromSmiles(smiles, sanitize=False) - if mol is None: - print(f"RDKit failed to at parsing {smiles} (returned None)") - else: - try: - mol = _sanitize_molecule(mol) - except Exception as e: - print(f"Rdkit failed at sanitizing {smiles}, \n Error: {e}") - return mol - def collate(self, list_of_tuples: list) -> any: """ Collate a list of samples into a batch. From a805f97cc5f37e8af146d736099b7dce51cd9eaf Mon Sep 17 00:00:00 2001 From: sfluegel Date: Mon, 3 Aug 2026 12:14:53 +0200 Subject: [PATCH 4/5] swap molecule sanitization for complete SMILES parsing --- .../fg_detection/fg_aware_rule_based.py | 8 ++--- .../preprocessing/reader/augmented_reader.py | 29 ++----------------- 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/chebai_graph/preprocessing/fg_detection/fg_aware_rule_based.py b/chebai_graph/preprocessing/fg_detection/fg_aware_rule_based.py index f4d27fa..4cf6f1e 100644 --- a/chebai_graph/preprocessing/fg_detection/fg_aware_rule_based.py +++ b/chebai_graph/preprocessing/fg_detection/fg_aware_rule_based.py @@ -8,7 +8,7 @@ from rdkit.Chem import AllChem from rdkit.Chem import MolToSmiles as m2s -from chebi_utils.sdf_extractor import _sanitize_molecule +from chebi_utils.read_molecule import smiles_or_inchi_to_mol from .fg_constants import ELEMENTS, FLAG_NO_FG @@ -1913,11 +1913,7 @@ def get_structure(mol): structure[frag] = {"atom": atom_idx, "is_ring_fg": False} # Convert fragment SMILES back to mol to match with fused ring atom indices - frag_mol = Chem.MolFromSmiles(frag, sanitize=False) - try: - frag_mol = _sanitize_molecule(frag_mol) - except Exception: - pass + frag_mol = smiles_or_inchi_to_mol(frag) frag_rings = frag_mol.GetRingInfo().AtomRings() if len(frag_rings) >= 1: structure[frag]["is_ring_fg"] = True diff --git a/chebai_graph/preprocessing/reader/augmented_reader.py b/chebai_graph/preprocessing/reader/augmented_reader.py index 986ad7d..002178a 100644 --- a/chebai_graph/preprocessing/reader/augmented_reader.py +++ b/chebai_graph/preprocessing/reader/augmented_reader.py @@ -4,7 +4,7 @@ import torch from chebai.preprocessing.reader import DataReader -from chebi_utils.sdf_extractor import _sanitize_molecule +from chebi_utils.read_molecule import smiles_or_inchi_to_mol from rdkit import Chem from torch_geometric.data import Data as GeomData @@ -75,7 +75,7 @@ def _read_data(self, raw_data: str | Chem.Mol) -> tuple[GeomData, dict] | None: RuntimeError: If an unexpected error occurs during graph augmentation. """ if isinstance(raw_data, str): - mol = self._smiles_to_mol(raw_data) + mol = smiles_or_inchi_to_mol(raw_data) smiles = raw_data else: mol = raw_data @@ -139,29 +139,6 @@ def _read_data(self, raw_data: str | Chem.Mol) -> tuple[GeomData, dict] | None: augmented_molecule, ) - def _smiles_to_mol(self, smiles: str) -> Chem.Mol | None: - """ - Converts a SMILES string to an RDKit molecule object. Sanitizes the molecule. - - Args: - smiles (str): SMILES string representing the molecule. - - Returns: - Chem.Mol | None: RDKit molecule object if successful, else None. - """ - mol = Chem.MolFromSmiles(smiles, sanitize=False) - if mol is None: - print(f"RDKit failed to parse {smiles} (returned None)") - self.f_cnt_for_smiles += 1 - else: - try: - mol = _sanitize_molecule(mol) - except Exception as e: - print(f"RDKit failed at sanitizing {smiles}, Error {e}") - self.f_cnt_for_smiles += 1 - mol = None - return mol - def _create_augmented_graph( self, mol: Chem.Mol ) -> tuple[torch.Tensor, dict] | None: @@ -315,7 +292,7 @@ def read_property( smiles = raw_data if smiles in self.mol_object_buffer: return property.get_property_value(self.mol_object_buffer[smiles]) - mol = self._smiles_to_mol(smiles) + mol = smiles_or_inchi_to_mol(smiles) if mol is None: return None try: From 427e6def51eac295117498f491b170065ffbd70c Mon Sep 17 00:00:00 2001 From: sfluegel Date: Mon, 3 Aug 2026 16:00:02 +0200 Subject: [PATCH 5/5] add chebi utils --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index a517cac..d27f1a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ authors = [ ] dependencies = [ "chebai", + "chebi_utils>=0.4", "descriptastorus", # below packages need to manually installed as mentioned in readme # torch-geometric