Fragment-SELFIES
June 29, 2026 ยท View on GitHub
Fragment-SELFIES is a robust, fragment-aware molecular language with validity-preserving decoding and explicit fragment structure. It records the BRICS fragment tree with a small set of structural tokens, while each fragment body is encoded as SELFIES-like tokens plus non-atomic Dummy attachment placeholders.
The current format is vocabulary-free at encode/decode time. It does not emit
large corpus-specific IDs such as [F:F000001]. Instead, it emits tokens like
[Frag], [Frag@0], [Attach:0], [pop], and SELFIES-compatible
fragment-body tokens such as [C], [=C], [Branch1], [Ring1], and [Dummy].
Full technical details are in docs/fragment_selfies.md.

Why This Format Exists
The fragment-token approach required a huge BRICS fragment vocabulary. For large molecular corpora, that produces large JSONL vocabulary artifacts and many rare fragment-ID tokens. The compact representation keeps the chemically meaningful BRICS decomposition but avoids fragment IDs by encoding each fragment body with SELFIES-compatible tokens.
This gives molecular language-model workflows a representation with:
- Explicit BRICS fragment tree structure.
- SELFIES-compatible tokens for fragment internals.
- A small fixed set of Fragment-SELFIES structural tokens.
- Canonical and randomized encoding modes.
- Randomized implicit-anchor augmentation for design pretraining.
- Direct decoding back to RDKit molecules.
- No required fragment vocabulary file for normal encode/decode.
Installation
Fragment-SELFIES supports Python 3.11 and newer.
python -m pip install fragment-selfies
RDKit is a runtime dependency. If a compatible RDKit wheel is unavailable for
your platform, install RDKit from conda-forge first, then install
Fragment-SELFIES with pip in the same environment.
Development install:
git clone https://github.com/fairydance/Fragment-SELFIES.git
cd Fragment-SELFIES
conda create -n fragment_selfies python=3.13
conda activate fragment_selfies
conda install -c conda-forge rdkit
python -m pip install -e ".[test]"
Verify the installation:
python -m pytest -q
Build and validate release artifacts:
python -m pip install --upgrade build twine
python -m build
python -m twine check dist/*
Token Grammar
Fragment-SELFIES strings are strict bracketed-token strings.
In one sentence: current compact Fragment-SELFIES starts root fragments with
[Frag], starts child fragments with [Frag@N], stores each fragment body as
SELFIES-compatible tokens, connects explicit BRICS tree edges with
[Attach:N][Frag@M], and represents implicit anchors as adjacent root
[Frag] blocks.
Example:
[Frag][C][C][O][C][Branch1][C][N][=C][Branch1][C][Dummy][C][=Ring1][#Branch1][C][Attach:0][Frag@1][O][=C][Branch1][C][Dummy][Dummy][Attach:0][Frag@0][C][O][Dummy][pop][pop]
Structural Fragment-SELFIES tokens:
| Token | Meaning |
|---|---|
[Frag] | Start a root BRICS fragment. Adjacent root fragments are disconnected anchors unless an explicit attachment edge is emitted. |
[Frag@N] | Start a child fragment and connect through child attachment N. |
[Attach:N] | Select parent attachment N on the current fragment. |
[pop] | Return traversal state to the parent fragment. |
[.] | Legacy disconnected molecule component marker. |
[SELFIES]...[ENDSELFIES] | Optional whole-molecule SELFIES fallback block. |
Everything between a fragment marker and the next structural token is the SELFIES-compatible body for that fragment.
The [Dummy...] tokens are non-atomic attachment placeholders. They are used
because the standard SELFIES encoder cannot encode RDKit dummy atoms (*)
directly, and they avoid colliding with real elements such as xenon.
Explicit And Implicit Anchor Semantics
Fragment-SELFIES uses only tokens that can be learned from the compact molecular corpus. This lets a pretrained tokenizer accept implicit-anchor prompts without adding new vocabulary.
There are no special implicit tokens. An implicit string is normal compact Fragment-SELFIES whose selected cut edge appears as adjacent roots:
Use adjacent root fragments as disconnected anchors:
[Frag]anchor_A[Frag]anchor_B
A concrete two-anchor shape is:
[Frag][C][=C][C][=C][C][=C][Ring1][=Branch1][Dummy][Frag][O][=C][Dummy]
Both anchors contain [Dummy] placeholders, which are the open attachment sites
used during implicit repair.
Use explicit attachment tokens when two fragments should be connected by the serialized tree itself:
[Frag]fragment_A[Attach:0][Frag@0]fragment_B
Do not use [.] for implicit-anchor prompts:
[Frag]anchor_A[.][Frag]anchor_B
That syntax means legacy disconnected components rather than adjacent anchors in the same repair group.
Decoding repairs implicit-anchor prompts by connecting adjacent root-anchor
components through unused [Dummy...] attachment points when possible. If no
valid attachment-based connection is possible, strict decoding raises and
non-strict decoding falls back to the largest valid component.
For linker-design generations that consume every generated-side [Dummy] but
still leave the first anchor disconnected, decoding can optionally repair one
missing return attachment. Enable repair_missing_return_attachment=True in the
API or --repair-missing-return-attachment in the CLI. The repair chooses the
farthest topologically suitable atom from the second anchor, tests the synthetic
single-bond return connection with RDKit sanitization, and leaves decoding
unchanged when no strictly valid site exists.
Encoding Modes
Default encoding is deterministic for the input order but not canonicalized:
from fragment_selfies import FragmentSelfiesCodec
codec = FragmentSelfiesCodec()
encoded = codec.encode("COC(=O)c1c(N)oc(C)c1C")
Canonical encoding gives a stable canonical string:
canonical = codec.encode("COC(=O)c1c(N)oc(C)c1C", canonical=True)
Randomized encoding changes atom order and fragment traversal for augmentation:
augmented = codec.encode("COC(=O)c1c(N)oc(C)c1C", randomized=True, seed=0)
Randomized encoding also supports implicit-anchor augmentation. With probability
implicit_probability, up to max_implicit_cuts BRICS edges in each connected component are
serialized as adjacent root anchors instead of an explicit [Attach:N][Frag@M]
edge. The default max_implicit_cuts=1 preserves the two-root implicit behavior;
set it to 2 or higher to produce multi-root implicit samples for multi-fragment
design training:
implicit_augmented = codec.encode(
"COC(=O)c1c(N)oc(C)c1C",
randomized=True,
seed=0,
implicit_probability=1.0,
max_implicit_cuts=2,
)
The default probability is 0.15 for randomized encoding. Set
implicit_probability=0.0 to disable implicit-anchor augmentation.
canonical=True and randomized=True are mutually exclusive.
CLI Usage
Encode one SMILES string:
conda run -n fragment_selfies fragment-selfies encode \
--smiles 'COC(=O)c1c(N)oc(C)c1C' \
--canonical
Encode a randomized augmentation:
conda run -n fragment_selfies fragment-selfies encode \
--smiles 'COC(=O)c1c(N)oc(C)c1C' \
--randomized \
--seed 0 \
--implicit-probability 0.15 \
--max-implicit-cuts 2
Force explicit one-root serialization:
conda run -n fragment_selfies fragment-selfies encode \
--smiles 'COC(=O)c1c(N)oc(C)c1C' \
--randomized \
--style explicit
Force implicit adjacent-root serialization when a BRICS edge exists:
conda run -n fragment_selfies fragment-selfies encode \
--smiles 'COC(=O)c1c(N)oc(C)c1C' \
--canonical \
--style implicit \
--max-implicit-cuts 2
Re-serialize an existing Fragment-SELFIES string into another style:
conda run -n fragment_selfies fragment-selfies reserialize \
--fragment-selfies '[Frag][O][=CH0][Branch1][C][Dummy][Dummy][Frag][C][OH0][Dummy]' \
--style explicit \
--canonical
Decode a compact Fragment-SELFIES string:
conda run -n fragment_selfies fragment-selfies decode \
--fragment-selfies '[Frag][C][=C][C][=C][C][=C][Ring1][=Branch1]' \
--canonical
Decoding uses non-strict recovery mode by default for generated strings. Add
--strict to require exact Fragment-SELFIES decoding for validation.
For linker-design outputs missing a final return attachment, add the opt-in repair flag:
conda run -n fragment_selfies fragment-selfies decode \
--fragment-selfies '<generated-linker-fragment-selfies>' \
--repair-missing-return-attachment \
--canonical
API Usage
from rdkit import Chem
from fragment_selfies import FragmentSelfiesCodec
codec = FragmentSelfiesCodec()
encoded = codec.encode("COC(=O)c1c(N)oc(C)c1C", canonical=True)
mol = codec.decode(encoded)
smiles = Chem.MolToSmiles(mol, canonical=True, isomericSmiles=False)
codec.decode(...) is non-strict by default. Pass strict=True when validation
should fail on malformed Fragment-SELFIES instead of recovering a molecule.
Pass repair_missing_return_attachment=True to enable the conservative linker
return-attachment repair described above.
SMILES fragments with dummy attachment atoms can be encoded as a single Fragment-SELFIES fragment and decoded with dummy atoms preserved:
fragment = codec.encode_fragment("[*]C(=O)O", canonical=True)
fragment_mol = codec.decode_fragment(fragment)
fragment_smiles = Chem.MolToSmiles(fragment_mol, canonical=True, isomericSmiles=False)
Use the CLI commands fragment-selfies encode-fragment and
fragment-selfies decode-fragment for the same fragment-specific conversion.
Example
Input SMILES:
COC(=O)c1c(N)oc(C)c1C
Canonical compact Fragment-SELFIES:
[Frag][C][C][O][C][Branch1][C][N][=C][Branch1][C][Dummy][C][=Ring1][#Branch1][C][Attach:0][Frag@1][O][=C][Branch1][C][Dummy][Dummy][Attach:0][Frag@0][C][O][Dummy][pop][pop]
The string has 31 tokens and 3 BRICS fragments. It decodes back to:
COC(=O)c1c(N)oc(C)c1C
Optional Fragment Statistics
Normal compact encode/decode does not need a BRICS fragment vocabulary. The
historical build-vocab command remains available as a BRICS fragment counting
and analysis utility.
conda run --live-stream -n fragment_selfies fragment-selfies build-vocab \
--input /path/to/molecules.smi \
--output /path/to/fragment_vocab.jsonl \
--min-count 1 \
--workers 8 \
--canonical \
--progress
Verification
conda run --live-stream -n fragment_selfies python -m py_compile src/fragment_selfies/*.py tests/test_fragment_selfies.py
conda run --live-stream -n fragment_selfies python -m pytest -q
Current expected result:
37 passed, 1 skipped
An optional corpus roundtrip test runs when FRAGMENT_SELFIES_SAMPLE_SMI points
to a local .smi file.
Molecular Language Model Integration
A downstream model should train its tokenizer directly on compact Fragment-SELFIES corpus files. The tokenizer should learn bracketed tokens from the generated corpus: Fragment-SELFIES structural tokens, SELFIES-compatible fragment-body tokens, and any task-specific control tokens. No large BRICS fragment-ID vocabulary is required.
Recommended integration path:
- Convert molecule corpora from SMILES to compact Fragment-SELFIES.
- Train a WordLevel tokenizer on the compact Fragment-SELFIES corpus.
- Use
FragmentSelfiesCodec.encode()for SMILES to Fragment-SELFIES conversion. - Use
FragmentSelfiesCodec.decode()plusChem.MolToSmiles()for generated molecule recovery.
Fragment-SELFIES is the molecular language used by Molexar, a unified multimodal molecular foundation model for drug design.
Citation
@misc{lin2026molexarunifiedmultimodalmolecular,
title={Molexar: A Unified Multimodal Molecular Foundation Model for Drug Design},
author={Haoyu Lin and Yiyan Liao and Jinmei Pan and Xinliao Ling and Luhua Lai and Jianfeng Pei},
year={2026},
eprint={2606.25865},
archivePrefix={arXiv},
primaryClass={q-bio.BM},
url={https://arxiv.org/abs/2606.25865},
}
License
Fragment-SELFIES is released under the MIT License. See LICENSE for details.