--- license: cc-by-nc-4.0 --- # BioXMol — molecular encoder A graph neural network that turns a SMILES string into a fixed-length embedding. It was trained with soft contrastive learning to align molecular structure with biological readouts (Cell Painting morphology from JUMP-CP and L1000 transcriptomics), so the embeddings carry biological signal useful for tasks like drug-induced liver injury (DILI) prediction. This repo ships the molecule encoder only — you provide SMILES. This is a custom GNN, **not** a `transformers` model, so `AutoModel.from_pretrained` does not work. Use the block below; it depends only on `torch` and `rdkit`. ![BioXMol overview](overview.png) ## How to use Install: ```bash pip install torch rdkit safetensors huggingface_hub numpy ``` Copy this whole block and run it — it downloads everything it needs from this repo: ```python import os, sys, torch from huggingface_hub import hf_hub_download from safetensors.torch import load_file REPO = "arslanmasood/BioXMol" # 1. download the code + weights from this repo for f in ["modeling_bioxmol.py", "featurizer.py"]: code_dir = os.path.dirname(hf_hub_download(REPO, f)) sys.path.insert(0, code_dir) from modeling_bioxmol import GatedGraphNeuralNetwork from featurizer import smiles2graph # 2. load the encoder model = GatedGraphNeuralNetwork(n_edge=1, in_dim=75, n_conv=6, fc_dims=[1024, 128]) model.load_state_dict(load_file(hf_hub_download(REPO, "bioxmol_soft_seed0.safetensors"))) model.eval() # 3. encode a list of SMILES -> embeddings @torch.no_grad() def encode(smiles_list, layer="first_fc"): graphs = [smiles2graph(s) for s in smiles_list] n = max(nf.shape[0] for _, nf in graphs) # pad to the largest molecule A, F, M = [], [], [] for adj, nf in graphs: adj = torch.as_tensor(adj, dtype=torch.float) nf = torch.as_tensor(nf, dtype=torch.float) a = torch.zeros(n, n); a[:adj.shape[0], :adj.shape[1]] = adj f = torch.zeros(n, nf.shape[1]); f[:nf.shape[0]] = nf m = torch.zeros(n, 1); m[:nf.shape[0]] = 1.0 # 1 = real atom A.append(a); F.append(f); M.append(m) return model.embed(torch.stack(A), torch.stack(F), torch.stack(M), layer=layer) # example emb = encode(["CCO", "CC(=O)Oc1ccccc1C(=O)O"]) print(emb.shape) # torch.Size([2, 1024]) ``` `layer` can be - `"GNN"` (75-d), - `"first_fc"` (1024-d, the default and recommended), - `"second_fc"` (128-d). ## Training data Pretrained on JUMP-CP (Cell Painting) and LINCS L1000 (transcriptomics); evaluated on DILIRank 2.0. Each dataset has its own terms — check them before redistributing derived artifacts. ## License CC-BY-NC-4.0 (attribution, non-commercial). The training code inherits GPLv3 from the upstream framework and is distributed separately. ## Citation ```bibtex @misc{masood2026bioxmol, title = {Unifying Disjoint Phenotypic Contexts: A Multimodal Soft Contrastive Approach to Identify DILI Activity Cliffs}, author = {Masood, Muhammad Arslan and Cui, Tianyu and Heinonen, Markus and Kaski, Samuel}, year = {2026}, doi = {10.21203/rs.3.rs-9875956/v1} } ```