Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
eb6b818
Remove unnecessary methods and attributes (#134)
c-w-feldmann Apr 22, 2025
aa5fac1
Merge branch 'main' into development
c-w-feldmann Apr 22, 2025
a1d1b79
Remove standard scaler from mol2float (#165)
c-w-feldmann Apr 23, 2025
6d801fe
Limit support to p311 and above (#166)
c-w-feldmann Apr 23, 2025
9a472e3
168 refactor moltofingerprint pipeline element (#169)
c-w-feldmann May 6, 2025
dd04bc2
Merge branch 'main' into development
c-w-feldmann May 6, 2025
35a1707
Merge branch 'main' into development
c-w-feldmann May 13, 2025
3486648
remove mol_counter from testing
c-w-feldmann May 13, 2025
c0c4acf
Merge branch 'main' into development
c-w-feldmann May 14, 2025
8eb012f
Merge branch 'main' into development
c-w-feldmann Jun 4, 2025
fa29248
Merge branch 'main' into development
c-w-feldmann Nov 28, 2025
306d14a
Merge branch 'main' into development
c-w-feldmann Dec 10, 2025
226f86a
Merge branch 'main' into development
c-w-feldmann Dec 19, 2025
aed0aa7
Merge branch 'main' into development
c-w-feldmann Dec 19, 2025
b12ab97
Merge branch 'main' into development
c-w-feldmann Jan 5, 2026
7b1d1a2
Merge branch 'main' into development
c-w-feldmann Jan 7, 2026
5fd727a
Remove deleted parameter from docs
c-w-feldmann Jan 6, 2026
69eb0b7
Ruff format
c-w-feldmann Jan 7, 2026
18f51cb
Merge branch 'main' into development
c-w-feldmann Jan 9, 2026
536f141
Merge branch 'main' into development
c-w-feldmann Jan 20, 2026
cacc4b7
fix line break
c-w-feldmann Jan 20, 2026
70659eb
autoformatting
c-w-feldmann Jan 20, 2026
5cdf4bb
fixes
c-w-feldmann Jan 20, 2026
a6eca3f
formatting
c-w-feldmann Jan 20, 2026
04c0d20
fix import
c-w-feldmann Jan 20, 2026
b276665
Mypy
c-w-feldmann Jan 20, 2026
002b808
Mypy
c-w-feldmann Jan 20, 2026
349b8a1
imports
c-w-feldmann Jan 20, 2026
6d3a5b6
Merge branch 'main' into development
c-w-feldmann Feb 12, 2026
73e96a4
Fix incorrectly transferred main change
c-w-feldmann Feb 12, 2026
2e9e573
Remove standardization
c-w-feldmann Feb 19, 2026
050cca6
Merge branch 'main' into development
c-w-feldmann Feb 19, 2026
55097fd
Typing
c-w-feldmann Feb 19, 2026
982b7d0
Merge branch 'main' into development
c-w-feldmann Feb 23, 2026
0283561
Merge branch 'main' into development
c-w-feldmann Feb 24, 2026
77f4e60
Autoformatting
c-w-feldmann Feb 24, 2026
c50eb7f
Merge branch 'main' into development
c-w-feldmann Feb 25, 2026
98ece67
Merge branch 'main' into development
c-w-feldmann Feb 26, 2026
b4d3d10
Merge branch 'main' into development
c-w-feldmann Mar 3, 2026
9cfa0c8
remove standardizer
c-w-feldmann Mar 3, 2026
498c8e9
remove standardizer and formatting
c-w-feldmann Mar 3, 2026
a80b129
merge main
c-w-feldmann Mar 23, 2026
3ce7184
Merge branch 'main' into development
c-w-feldmann Mar 26, 2026
58478e5
Merge branch 'main' into development
c-w-feldmann May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions molpipeline/abstract_pipeline_elements/any2mol/string2mol.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,14 @@ def pretransform_single(self, value: str) -> OptionalMol:
Rdkit molecule if valid string representation, else None.

"""
if value is None:
return InvalidInstance(
self.uuid,
f"Invalid representation: {value}",
self.name,
)

if not isinstance(value, str):
return InvalidInstance(
self.uuid,
f"Not a string: {value}",
self.name,
)

mol: RDKitMol = self.string_to_mol(value)
mol = self.string_to_mol(value)

if not mol:
return InvalidInstance(
Expand All @@ -92,7 +85,7 @@ def pretransform_single(self, value: str) -> OptionalMol:
return mol

@abc.abstractmethod
def string_to_mol(self, value: str) -> RDKitMol:
def string_to_mol(self, value: str) -> RDKitMol | None:
"""Transform string representation to molecule.

Parameters
Expand All @@ -102,7 +95,7 @@ def string_to_mol(self, value: str) -> RDKitMol:

Returns
-------
RDKitMol
RDKitMol | None
Rdkit molecule if valid representation, else None.

"""
220 changes: 54 additions & 166 deletions molpipeline/abstract_pipeline_elements/mol2any/mol2bitvector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

import abc
import copy
from collections.abc import Iterable
from typing import Any, Literal, Self, TypeAlias, get_args, overload
from collections.abc import Iterable, Mapping, Sequence
from typing import (
Any,
Literal,
Self,
TypeAlias,
get_args,
overload,
)

import numpy as np
import numpy.typing as npt
Expand All @@ -19,7 +26,7 @@
from molpipeline.abstract_pipeline_elements.core import MolToAnyPipelineElement
from molpipeline.utils.matrices import sparse_from_index_value_dicts
from molpipeline.utils.molpipeline_types import RDKitMol
from molpipeline.utils.substructure_handling import CircularAtomEnvironment
from molpipeline.utils.substructure_handling import AtomEnvironment

# possible output types for a fingerprint:
# - "sparse" is a sparse csr_matrix
Expand Down Expand Up @@ -74,7 +81,7 @@


class MolToFingerprintPipelineElement(MolToAnyPipelineElement, abc.ABC):
"""Abstract class for PipelineElements which transform molecules to fingerprints."""
"""Abstract PipelineElement which transform molecules to fingerprints."""

_n_bits: int
_feature_names: list[str]
Expand All @@ -101,9 +108,9 @@ def __init__(
and parameters.
name: str
Name of PipelineElement.
n_jobs:
n_jobs: int, default=1
Number of jobs.
uuid: Optional[str]
uuid: str | None, optional
Unique identifier.

"""
Expand Down Expand Up @@ -295,8 +302,42 @@ def pretransform_single(
class MolToRDKitGenFPElement(MolToFingerprintPipelineElement, abc.ABC):
"""Abstract class for PipelineElements using the FingeprintGenerator64."""

@property
def n_bits(self) -> int:
"""Get number of bits in (or size of) fingerprint."""
return self._n_bits

@n_bits.setter
def n_bits(self, value: int) -> None:
"""Set number of bits in Morgan fingerprint.

Parameters
----------
value: int
Number of bits in Morgan fingerprint.

Raises
------
ValueError
If value is not a positive integer.

"""
if not isinstance(value, int) or value < 1:
raise ValueError(
f"Number of bits has to be a integer > 0! (Received: {value})",
)
self._n_bits = value

@property
def output_type(self) -> str:
"""Get output type."""
if self.counted:
return "integer"
return "binary"

def __init__(
self,
n_bits: int = 2048,
counted: bool = False,
return_as: FPReturnAsOption = "sparse",
name: str = "MolToRDKitGenFin",
Expand All @@ -307,6 +348,8 @@ def __init__(

Parameters
----------
n_bits: int, default=2048
Number of bits in fingerprint.
counted: bool, default=False
Whether to count the bits or not.
return_as: FPReturnAsOption, default="sparse"
Expand All @@ -325,6 +368,7 @@ def __init__(
n_jobs=n_jobs,
uuid=uuid,
)
self.n_bits = n_bits
self.counted = counted

@abc.abstractmethod
Expand Down Expand Up @@ -417,158 +461,11 @@ def set_params(self, **parameters: Any) -> Self:
super().set_params(**parameter_dict_copy)
return self


class ABCMorganFingerprintPipelineElement(MolToRDKitGenFPElement, abc.ABC):
"""Abstract Class for Morgan fingerprints."""

@property
def output_type(self) -> str:
"""Get output type."""
if self.counted:
return "integer"
return "binary"

# pylint: disable=R0913
def __init__(
self,
radius: int = 2,
use_features: bool = False,
counted: bool = False,
return_as: FPReturnAsOption = "sparse",
name: str = "AbstractMorgan",
n_jobs: int = 1,
uuid: str | None = None,
) -> None:
"""Initialize abstract class.

Parameters
----------
radius: int, default=2
Radius of fingerprint.
use_features: bool, default=False
Whether to represent atoms by element or category (donor, acceptor, etc.)
counted: bool, default=False
Whether to count the bits or not.
return_as: FPReturnAsOption, default="sparse"
Type of output.
When "sparse" the fingerprints will be returned as a scipy.sparse.csr_matrix
holding a sparse representation of the bit vectors.
With "dense" a numpy matrix will be returned.
With "rdkit" the fingerprints will be returned as a list of
RDKit's data structure, like ExplicitBitVect, IntSparseBitVect, etc.
name: str, default="AbstractMorgan"
Name of PipelineElement.
n_jobs: int, default=1
Number of jobs.
uuid: str | None, optional
Unique identifier.

Raises
------
ValueError
If radius is not a positive integer.

"""
# pylint: disable=R0801
super().__init__(
return_as=return_as,
counted=counted,
name=name,
n_jobs=n_jobs,
uuid=uuid,
)
self._use_features = use_features
if isinstance(radius, int) and radius >= 0:
self._radius = radius
else:
raise ValueError(
f"Number of bits has to be a positive integer! (Received: {radius})",
)

def get_params(self, deep: bool = True) -> dict[str, Any]:
"""Get object parameters relevant for copying the class.

Parameters
----------
deep: bool
If True get a deep copy of the parameters.

Returns
-------
dict[str, Any]
Dictionary of parameter names and values.

"""
parameters = super().get_params(deep)
if deep:
parameters["radius"] = copy.copy(self.radius)
parameters["use_features"] = copy.copy(self.use_features)
else:
parameters["radius"] = self.radius
parameters["use_features"] = self.use_features

# remove fill_value from parameters
parameters.pop("fill_value", None)
return parameters

def set_params(self, **parameters: Any) -> Self:
"""Set parameters.

Parameters
----------
parameters: Any
Dictionary of parameter names and values.

Returns
-------
Self
PipelineElement with updated parameters.

"""
parameter_copy = dict(parameters)
radius = parameter_copy.pop("radius", None)
use_features = parameter_copy.pop("use_features", None)

# explicitly check for None, since 0 is a valid value
if radius is not None:
self._radius = radius
# explicitly check for None, since False is a valid value
if use_features is not None:
self._use_features = bool(use_features)
super().set_params(**parameter_copy)
return self

@property
def radius(self) -> int:
"""Get radius of Morgan fingerprint."""
return self._radius

@property
def use_features(self) -> bool:
"""Get whether to encode atoms by features or not."""
return self._use_features

@abc.abstractmethod
def _explain_rdmol(self, mol_obj: RDKitMol) -> dict[int, list[tuple[int, int]]]:
"""Get central atom and radius of all features in molecule.

Parameters
----------
mol_obj: RDKitMol
RDKit molecule to be encoded.

Returns
-------
dict[int, list[tuple[int, int]]]
Dictionary with mapping from bit to atom index and radius.

"""
raise NotImplementedError

def bit2atom_mapping(
self,
mol_obj: RDKitMol,
) -> dict[int, list[CircularAtomEnvironment]]:
) -> Mapping[int, Sequence[AtomEnvironment]]:
"""Obtain set of atoms for all features.

Parameters
Expand All @@ -578,18 +475,9 @@ def bit2atom_mapping(

Returns
-------
dict[int, list[CircularAtomEnvironment]]
Dictionary with mapping from bit to encoded AtomEnvironments
Mapping[int, Sequence[AtomEnvironment]]
Dictionary with mapping from bit to encoded
AtomEnvironments
(which contain atom indices).

"""
bit2atom_dict = self._explain_rdmol(mol_obj)
result_dict: dict[int, list[CircularAtomEnvironment]] = {}
# Iterating over all present bits and respective matches
for bit, matches in bit2atom_dict.items(): # type: int, list[tuple[int, int]]
result_dict[bit] = []
for central_atom, radius in matches: # type: int, int
env = CircularAtomEnvironment.from_mol(mol_obj, central_atom, radius)
result_dict[bit].append(env)
# Transforming default dict to dict
return result_dict
Loading