molforge.chem¶
chem ¶
Cheminformatics operations on :class:~molforge.core.Molecule.
Where :mod:molforge.core holds the small-molecule type and
:mod:molforge.io reads molecules from files, this package holds the
chemistry operations — standardization (cleaning) for consistent,
deduplicable structures, descriptors, quality/dedup, and Bemis-Murcko
scaffold extraction for series- and diversity-level analysis. Everything
here is RDKit-backed and lazy: importing :mod:molforge.chem never pulls RDKit in, and an operation
without RDKit raises :class:~molforge.core.RDKitNotInstalledError.
MoleculeDataset ¶
A lazy, immutable pipeline over a stream of molecules.
Wrap any iterable of :class:~molforge.core.Molecule; the combinators
(:meth:map, :meth:take) return new datasets and nothing runs until
the dataset is iterated or :meth:collect-ed.
Attributes are intentionally hidden: a dataset is defined only by what it yields when iterated.
Wrap an iterable of molecules (not consumed until iterated).
map ¶
Apply fn to every molecule, lazily.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[Molecule], Molecule]
|
A per-molecule transform, e.g. :func: |
required |
Returns:
| Type | Description |
|---|---|
MoleculeDataset
|
A new dataset yielding |
take ¶
Keep only the first n molecules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
How many molecules to keep; |
required |
Returns:
| Type | Description |
|---|---|
MoleculeDataset
|
A new dataset yielding at most |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
filter ¶
Keep molecules whose descriptors satisfy criterion.
criterion is a :class:~molforge.validation.Criterion over
molecule descriptors — see :func:molforge.chem.molecule_descriptors
for the vocabulary (molecular_weight, formal_charge,
n_atoms, n_heavy_atoms). Its referenced names are validated up
front, and only those descriptors are computed per molecule::
from molforge.validation import Criterion
ds.filter(Criterion.lt("molecular_weight", 500) & Criterion.le("formal_charge", 0))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
criterion
|
Criterion
|
A criterion over descriptor names. |
required |
Returns:
| Type | Description |
|---|---|
MoleculeDataset
|
A new dataset yielding only the molecules that satisfy |
MoleculeDataset
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the criterion references an unknown descriptor. |
RDKitNotInstalledError
|
If RDKit isn't installed (on consumption). |
valid ¶
Keep only molecules that pass RDKit sanitization.
A lazy filter over :func:molforge.chem.is_valid — structures RDKit
rejects are dropped rather than raising.
Returns:
| Type | Description |
|---|---|
MoleculeDataset
|
A new dataset yielding only the valid molecules. |
dedup ¶
Drop duplicate molecules by structural identity, keeping the first.
Streams with a running set of seen identities, so only the identities (not the molecules) are held in memory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Identity to compare on — |
'inchikey'
|
Returns:
| Type | Description |
|---|---|
MoleculeDataset
|
A new dataset yielding the first molecule of each identity, in |
MoleculeDataset
|
order. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
group_by_scaffold ¶
Group the molecules by Bemis-Murcko scaffold.
A terminal operation, like :meth:collect: grouping can't know a
scaffold is complete until the stream ends, so this consumes the
dataset and materializes the groups. Use it to size a library's
chemical series, measure scaffold diversity, or pick representatives
— where :meth:dedup with key="scaffold" keeps only the first
molecule of each series and stays lazy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
generic
|
bool
|
Group on the generic framework (every atom a carbon, every bond single), so scaffolds differing only in their heteroatoms — benzene and pyridine — land in one group. |
False
|
Returns:
| Type | Description |
|---|---|
dict[str, list[Molecule]]
|
|
dict[str, list[Molecule]]
|
each list in dataset order. Acyclic molecules have no scaffold |
dict[str, list[Molecule]]
|
and share the |
Raises:
| Type | Description |
|---|---|
RDKitNotInstalledError
|
If RDKit isn't installed. |
Example
Size a library's chemical series, then inspect one of them::
groups = MoleculeDataset(library).group_by_scaffold()
len(groups) # 41 distinct scaffolds
[m.name for m in groups["c1ccccc1"]] # ['aspirin', 'paracetamol']
collect ¶
Materialize the dataset into a list, running the whole pipeline.
molecule_descriptors ¶
molecule_descriptors(
molecule: Molecule,
*,
names: Iterable[str] | None = None,
) -> dict[str, Any]
Compute filterable descriptors for a molecule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
molecule
|
Molecule
|
The molecule to describe. |
required |
names
|
Iterable[str] | None
|
Which descriptors to compute; defaults to all of
:data: |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A flat |
dict[str, Any]
|
meth: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a requested name isn't a known descriptor. |
RDKitNotInstalledError
|
If RDKit isn't installed. |
is_valid ¶
Whether molecule passes RDKit sanitization.
Sanitization (valence, aromaticity, kekulization) runs on a copy, so the molecule is never mutated. A structure RDKit rejects — a pentavalent carbon, an unkekulizable ring — is reported as invalid rather than raising, so this reads as a predicate you can filter a set on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
molecule
|
Molecule
|
The molecule to check. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Raises:
| Type | Description |
|---|---|
RDKitNotInstalledError
|
If RDKit isn't installed. |
unique ¶
Deduplicate molecules by structural identity, keeping the first seen.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
molecules
|
Iterable[Molecule]
|
The molecules to deduplicate. |
required |
key
|
str
|
Which identity to compare on — |
'inchikey'
|
Returns:
| Type | Description |
|---|---|
list[Molecule]
|
A new list with duplicates removed, preserving input order and |
list[Molecule]
|
keeping the first molecule of each identity. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
RDKitNotInstalledError
|
If RDKit isn't installed. |
murcko_scaffold ¶
Extract a molecule's Bemis-Murcko scaffold.
Returns a new :class:~molforge.core.Molecule, leaving the input
untouched, and preserves its name while noting the extraction in
metadata["scaffold"] ("murcko", or "murcko_generic" when
generic) — the same convention the standardization ops follow.
An acyclic molecule has no scaffold, so the result is an empty molecule
(n_atoms == 0, smiles == ""). That is Bemis-Murcko's own
convention, not an error; a set of acyclic molecules therefore shares a
single empty scaffold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
molecule
|
Molecule
|
The molecule to reduce (left unmodified). |
required |
generic
|
bool
|
Return the generic framework instead — every atom becomes a carbon and every bond a single bond, so scaffolds that differ only in their heteroatoms or bond orders (benzene vs. pyridine) come out identical. Useful for coarser, topology-only grouping. |
False
|
Returns:
| Type | Description |
|---|---|
Molecule
|
The scaffold as a new :class: |
Raises:
| Type | Description |
|---|---|
RDKitNotInstalledError
|
If RDKit isn't installed. |
Example
from molforge.core import Molecule from molforge.chem import murcko_scaffold aspirin = Molecule.from_smiles("CC(=O)OC1=CC=CC=C1C(=O)O") murcko_scaffold(aspirin).smiles 'c1ccccc1' nicotinamide = Molecule.from_smiles("c1ccncc1C(N)=O") murcko_scaffold(nicotinamide).smiles # pyridine, amide stripped 'c1ccncc1' murcko_scaffold(nicotinamide, generic=True).smiles # same frame as benzene 'C1CCCCC1'
canonical_tautomer ¶
Convert to RDKit's canonical tautomer.
cleanup ¶
Sanitize, normalize functional groups, and reionize.
largest_fragment ¶
Keep the largest organic fragment — strips salts and solvents.
neutralize ¶
Remove formal charges where chemically reasonable.