Skip to main content

Robust fragment-aware molecular language with SELFIES-compatible fragment bodies.

Project description

Fragment-SELFIES

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.

Fragment-SELFIES codec overview

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:

  1. Convert molecule corpora from SMILES to compact Fragment-SELFIES.
  2. Train a WordLevel tokenizer on the compact Fragment-SELFIES corpus.
  3. Use FragmentSelfiesCodec.encode() for SMILES to Fragment-SELFIES conversion.
  4. Use FragmentSelfiesCodec.decode() plus Chem.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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fragment_selfies-1.0.0.tar.gz (337.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fragment_selfies-1.0.0-py3-none-any.whl (24.9 kB view details)

Uploaded Python 3

File details

Details for the file fragment_selfies-1.0.0.tar.gz.

File metadata

  • Download URL: fragment_selfies-1.0.0.tar.gz
  • Upload date:
  • Size: 337.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for fragment_selfies-1.0.0.tar.gz
Algorithm Hash digest
SHA256 c9b3e5d77bc8d11e04c979ce7af069ac5a7e23ed9892b88279875bb92bfdb3c6
MD5 7df245b9ddb2a20456c213573423f0a0
BLAKE2b-256 d3add5521721d9a5910f133252f3f01092d8a5cd3ff1722124c0d260970a3244

See more details on using hashes here.

File details

Details for the file fragment_selfies-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fragment_selfies-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4b51dd85726139a3ca57ac63a2d407234aebcf22f61296342a0a8b1b89ab57b9
MD5 b529b9aa0f52b8f65373745c679d8c85
BLAKE2b-256 58a3e3a8a5c7445ad18e9fe3162baf73e0c1787a003016fd1edf0fa9b7018eb0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page