Skill Tracer
Skills › Scientific Agent Skills › datamol › fragments_scaffolds.md

fragments_scaffolds.md

83 lines · skills/datamol/references/fragments_scaffolds.md at 5f2bc03

Open on GitHub
1# Datamol fragments and scaffolds (0.13.0)
2
3Sources: [fragment API](https://docs.datamol.io/stable/api/datamol.fragment.html),
4[scaffold API](https://docs.datamol.io/stable/api/datamol.scaffold.html), and released source.
5

Datamol fragments and scaffolds (0.13.0)

Sources: fragment API, scaffold API, and released source.

Raw lines 1 to 5
6## Murcko frameworks and split boundaries
7
8```python
9import datamol as dm
10from collections import Counter
11mol = dm.to_mol("c1ccc(cc1)CCN") # phenethylamine
12scaffold = dm.to_scaffold_murcko(mol)
13assert dm.to_smiles(scaffold) == "c1ccccc1" # terminal side chain is removed
14mols = [dm.to_mol(s) for s in ["c1ccccc1CCN", "c1ccccc1O", "c1ccncc1"]]
15scaffold_smiles = [dm.to_smiles(dm.to_scaffold_murcko(m)) for m in mols]
16scaffold_counts = Counter(scaffold_smiles)
17```
19Murcko frameworks retain rings and linkers between rings. A chain attached to one ring
20is a side chain, not a retained linker. Acyclic molecules have an empty scaffold; choose
21and report their grouping policy explicitly. `make_generic=True` additionally abstracts
22atom/bond chemistry. Standardize consistently before grouping and keep full molecule IDs.
23Scaffold-disjoint splits do not by themselves prevent duplicates, analog leakage, target
24leakage, or temporal leakage; check overlap and group sizes.
26`dm.scaffold.fuzzy_scaffolding(mols, enforce_subs=None, n_atom_cuttoff=8, ...)`
27accepts a **list** of molecules. It returns `(scaffold_smiles_set, scaffold_infos,
28scaffold_to_group)`: the last two are pandas DataFrames in 0.13.0. `enforce_subs` contains substructure patterns;
29`additional_templates` supplies Mol templates. MCS and R-group decomposition settings
30matter; this is a template-generation tool, not a unique scaffold ontology. It removes
31conformers from molecules during decomposition; pass `[dm.copy_mol(m) for m in mols]`
32when the source coordinates must survive.

Murcko frameworks and split boundaries

import datamol as dm
from collections import Counter
mol = dm.to_mol("c1ccc(cc1)CCN")  # phenethylamine
scaffold = dm.to_scaffold_murcko(mol)
assert dm.to_smiles(scaffold) == "c1ccccc1"  # terminal side chain is removed
mols = [dm.to_mol(s) for s in ["c1ccccc1CCN", "c1ccccc1O", "c1ccncc1"]]
scaffold_smiles = [dm.to_smiles(dm.to_scaffold_murcko(m)) for m in mols]
scaffold_counts = Counter(scaffold_smiles)

Murcko frameworks retain rings and linkers between rings. A chain attached to one ring is a side chain, not a retained linker. Acyclic molecules have an empty scaffold; choose and report their grouping policy explicitly. make_generic=True additionally abstracts atom/bond chemistry. Standardize consistently before grouping and keep full molecule IDs. Scaffold-disjoint splits do not by themselves prevent duplicates, analog leakage, target leakage, or temporal leakage; check overlap and group sizes.

dm.scaffold.fuzzy_scaffolding(mols, enforce_subs=None, n_atom_cuttoff=8, ...) accepts a list of molecules. It returns (scaffold_smiles_set, scaffold_infos, scaffold_to_group): the last two are pandas DataFrames in 0.13.0. enforce_subs contains substructure patterns; additional_templates supplies Mol templates. MCS and R-group decomposition settings matter; this is a template-generation tool, not a unique scaffold ontology. It removes conformers from molecules during decomposition; pass [dm.copy_mol(m) for m in mols] when the source coordinates must survive.

Raw lines 6 to 33
34## BRICS, RECAP and MMPA contracts
36`dm.fragment.brics(mol, singlepass=True, remove_parent=False, sanitize=True, fix=True)`
37and `dm.fragment.recap(mol, remove_parent=False, sanitize=True, fix=True)` return
38**lists of Mol**, not sets of SMILES. Parent inclusion and dummy-atom fixing change the
39meaning of fragment counts. Request `remove_parent=True` to exclude the original molecule;
40use `fix=False` when attachment labels must survive. Do not strip attachment points by
41string replacement: that loses environment labels and can produce wrong chemistry.
43```python
44mol = dm.to_mol("CC(=O)Oc1ccccc1C(=O)O")
45brics_molecules = dm.fragment.brics(mol, remove_parent=True, fix=False)
46recap_molecules = dm.fragment.recap(mol, remove_parent=True, fix=False)
47brics_smiles = {dm.to_smiles(fragment) for fragment in brics_molecules}
48assert all(isinstance(fragment, dm.Mol) for fragment in brics_molecules)
49```
51BRICS uses compatibility rules between labelled environments; labels such as `[3*]` and
52`[16*]` are not interchangeable generic atoms. RECAP follows its own retrosynthetic bond
53rules. Neither is proof of a feasible synthesis or a reaction yield. Avoid claiming a fixed
54universal count of bond types: RDKit implementations encode environment/rule combinations.
55`dm.fragment.mmpa_frag(mol, max_cut=1, max_bond_cut=20)` returns a set of
56`(core_smiles, sidechain_smiles)` pairs in the release, despite its Mol type annotation.
57Its output alone is not a matched-pair dataset; matching requires comparing appropriate
58common cores across molecules and validating attachment correspondence.

BRICS, RECAP and MMPA contracts

dm.fragment.brics(mol, singlepass=True, remove_parent=False, sanitize=True, fix=True) and dm.fragment.recap(mol, remove_parent=False, sanitize=True, fix=True) return lists of Mol, not sets of SMILES. Parent inclusion and dummy-atom fixing change the meaning of fragment counts. Request remove_parent=True to exclude the original molecule; use fix=False when attachment labels must survive. Do not strip attachment points by string replacement: that loses environment labels and can produce wrong chemistry.

mol = dm.to_mol("CC(=O)Oc1ccccc1C(=O)O")
brics_molecules = dm.fragment.brics(mol, remove_parent=True, fix=False)
recap_molecules = dm.fragment.recap(mol, remove_parent=True, fix=False)
brics_smiles = {dm.to_smiles(fragment) for fragment in brics_molecules}
assert all(isinstance(fragment, dm.Mol) for fragment in brics_molecules)

BRICS uses compatibility rules between labelled environments; labels such as [3*] and [16*] are not interchangeable generic atoms. RECAP follows its own retrosynthetic bond rules. Neither is proof of a feasible synthesis or a reaction yield. Avoid claiming a fixed universal count of bond types: RDKit implementations encode environment/rule combinations. dm.fragment.mmpa_frag(mol, max_cut=1, max_bond_cut=20) returns a set of (core_smiles, sidechain_smiles) pairs in the release, despite its Mol type annotation. Its output alone is not a matched-pair dataset; matching requires comparing appropriate common cores across molecules and validating attachment correspondence.

Raw lines 34 to 59
60## Reproducible fragment counting and heuristic overlap
62```python
63def fragment_keys(molecule):
64 return {dm.to_smiles(f) for f in dm.fragment.brics(
65 molecule, remove_parent=True, fix=False,
66 )}
68# Each fragment contributes at most once per compound (prevalence, not site count).
69fragment_counts = Counter()
70for molecule in mols:
71 fragment_counts.update(fragment_keys(molecule))
72reference_fragments = fragment_keys(mol)
74def fragment_score(molecule, reference):
75 keys = fragment_keys(molecule)
76 return len(keys & reference) / len(keys) if keys else 0.0
78scores = [fragment_score(molecule, reference_fragments) for molecule in mols]
79```
81This overlap score measures the chosen fragmentation representation. It is not a validated
82activity prediction. Specify fragmentation options, duplicate policy, attachment treatment,
83and whether a parent molecule is included whenever comparing libraries.

Reproducible fragment counting and heuristic overlap

def fragment_keys(molecule):
    return {dm.to_smiles(f) for f in dm.fragment.brics(
        molecule, remove_parent=True, fix=False,
    )}

# Each fragment contributes at most once per compound (prevalence, not site count).
fragment_counts = Counter()
for molecule in mols:
    fragment_counts.update(fragment_keys(molecule))
reference_fragments = fragment_keys(mol)

def fragment_score(molecule, reference):
    keys = fragment_keys(molecule)
    return len(keys & reference) / len(keys) if keys else 0.0

scores = [fragment_score(molecule, reference_fragments) for molecule in mols]

This overlap score measures the chosen fragmentation representation. It is not a validated activity prediction. Specify fragmentation options, duplicate policy, attachment treatment, and whether a parent molecule is included whenever comparing libraries.

Raw lines 60 to 83