Metadata-Version: 2.4
Name: chebifier
Version: 1.2.2
Summary: An AI ensemble model for predicting chemical classes
Author-email: Simon Flügel <simon.fluegel@uni-osnabrueck.de>
License: MIT License
        
        Copyright (c) 2026 ChEB-AI
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click
Requires-Dist: pyyaml
Requires-Dist: tqdm
Requires-Dist: rdkit
Requires-Dist: jsonargparse[signatures]==4.27.7
Requires-Dist: scikit-learn
Provides-Extra: dev
Requires-Dist: black; extra == "dev"
Requires-Dist: isort; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Provides-Extra: models
Requires-Dist: chebai_graph==1.0.0; extra == "models"
Requires-Dist: chebai==1.2.0; extra == "models"
Requires-Dist: chemlog-extra==1.0.1; extra == "models"
Requires-Dist: c3p; extra == "models"
Dynamic: license-file

# python-chebifier
An AI ensemble model for predicting chemical classes in the ChEBI ontology. It integrates deep learning models,
rule-based models and generative AI-based models.

A web application for Chebifier is available at https://chebifier.hastingslab.org/.

## Installation

You can get the package from PyPI:
```bash
pip install chebifier
```

or get the latest development version from GitHub:
```bash
# Clone the repository
git clone https://github.com/yourusername/python-chebifier.git
cd python-chebifier

# Install the package
pip install -e .
```

By default, the models of the ensemble are not installed. 
You can install them with the `[models]` extra:
```bash
pip install chebifier[models]
```

The Graph Neural Networks depend on `torch_geometric` and `torch_scatter` which you need to install separately ([depending on your CUDA version](https://pytorch-geometric.readthedocs.io/en/latest/install/installation.html)). E.g.
```bash
pip install torch==2.12.0 torch_scatter torch_geometric -f https://data.pyg.org/whl/torch-2.12.0+cpu.html
```

## Usage

### Command Line Interface

The package provides a command-line interface (CLI) for making predictions using an ensemble model.

The ensemble configuration is given by a configuration file (by default, this is `chebifier/ensemble.yml`). If you
want to change which models are included in the ensemble or how they are weighted, you can create your own configuration file.

Trained deep learning models are automatically downloaded from [Hugging Face](https://huggingface.co/chebai).
To access a model from Hugging face, add the `load_model` key in your configuration file. For example:

```yaml
my_electra:
  type: electra
  load_model: "electra_chebi50-3star_v244"
```

### Available model weights:

* `resgated-aug_chebi50-3star_v244`
* `gat-aug_chebi50_v244`
* `electra_chebi50-3star_v244`
* `gat_chebi50_v244`
* `electra_chebi50_v241`
* `resgated_chebi50_v241`
* `c3p_with_weights`


You can also supply your own model checkpoints (see `configs/example_config.yml` for an example).

```bash
# Make predictions
python -m chebifier predict --smiles "CC(=O)OC1=CC=CC=C1C(=O)O" --smiles "C1=CC=C(C=C1)C(=O)O"

# Make predictions using SMILES from a file
python -m chebifier predict --smiles-file smiles.txt

# Make predictions using a configuration file
python -m chebifier predict --ensemble-config configs/my_config.yml --smiles-file smiles.txt

# Get all available options
python -m chebifier predict --help
```

### Python API

You can use the package programmatically as well:

```python
from chebifier import BaseEnsemble

# Instantiate ensemble model. Optionally, you can pass
# a path to a configuration, like 'configs/example_config.yml'
ensemble = BaseEnsemble()

# Make predictions
smiles_list = ["CC(=O)OC1=CC=CC=C1C(=O)O", "C1=CC=C(C=C1)C(=O)O"]
predictions = ensemble.predict_smiles_list(smiles_list)

# Print results
for smiles, prediction in zip(smiles_list, predictions):
    print(f"SMILES: {smiles}")
    if prediction:
        print(f"Predicted classes: {prediction}")
    else:
        print("No predictions")
```

### The models
Currently, the following models are supported:


| Model | Description | #Classes | Publication                                                           | Repository                                                                            |
|-------|-------------|----------|-----------------------------------------------------------------------|----------------------------------------------------------------------------------------|
| `electra` | A transformer-based deep learning model trained on ChEBI SMILES strings. | 1531*  | [Glauer, Martin, et al., 2024: Chebifier: Automating semantic classification in ChEBI to accelerate data-driven discovery, Digital Discovery 3 (2024) 896-907](https://pubs.rsc.org/en/content/articlehtml/2024/dd/d3dd00238a) | [python-chebai](https://github.com/ChEB-AI/python-chebai) |
| `resgated` | A Residual Gated Graph Convolutional Network trained on ChEBI molecules. | 1531* | | [python-chebai-graph](https://github.com/ChEB-AI/python-chebai-graph) |
| `gat` | A Graph Attention Network trained on ChEBI molecules. | 1531* | | [python-chebai-graph](https://github.com/ChEB-AI/python-chebai-graph) |
| `chemlog_peptides` | A rule-based model specialised on peptide classes. | 18 | [Flügel, Simon, et al., 2025: ChemLog: Making MSOL Viable for Ontological Classification and Learning, arXiv](https://arxiv.org/abs/2507.13987) | [chemlog-peptides](https://github.com/sfluegel05/chemlog-peptides) |
| `chemlog_element`, `chemlog_organox` | Extensions of ChemLog for classes that are defined either by the presence of a specific element or by the presence of an organic bond. | 118 + 37 | | [chemlog-extra](https://github.com/ChEB-AI/chemlog-extra) |
| `c3p` | A collection _Chemical Classifier Programs_, generated by LLMs based on the natural language definitions of ChEBI classes. | 338 | [Mungall, Christopher J., et al., 2025: Chemical classification program synthesis using generative artificial intelligence, Journal of Cheminsformatics](https://link.springer.com/article/10.1186/s13321-025-01092-3) | [c3p](https://github.com/chemkg/c3p) |

In addition, Chebifier also includes a ChEBI lookup that automatically retrieves the ChEBI superclasses for a class
matched by a SMILES string. This is not activated by default, but can be included by adding
```yaml
chebi_lookup:
    type: chebi_lookup
    model_weight: 10 # optional
```
to your configuration file.

### The ensemble
For an extended description of the ensemble, see [Flügel, Simon, et al., 2025: Chebifier 2: An Ensemble for Chemistry](https://ceur-ws.org/Vol-4064/SymGenAI4Sci-paper4.pdf).

<img width="700" alt="ensemble_architecture" src="https://github.com/user-attachments/assets/9275d3cd-ac88-466f-a1e9-27d20d67543b" />

Given a sample (i.e., a SMILES string) and models $m_1, m_2, \ldots, m_n$, the ensemble works as follows:
1. Get predictions from each model $m_i$ for the sample.
2. For each class $c$, aggregate predictions $p_c^{m_i}$ from all models that made a prediction for that class.
The aggregation happens separately for all positive predictions (i.e., $p_c^{m_i} \geq 0.5$) and all negative predictions
($p_c^{m_i} < 0.5$). If the aggregated value is larger for the positive predictions than for the negative predictions,
the ensemble makes a positive prediction for class $c$:

<img width="2297" height="114" alt="image" src="https://github.com/user-attachments/assets/2f0263ae-83ac-41ea-938a-c71b46082c22" />
<!-- For some reason, this formula does not render in GitHub markdown. Therefore, I rendered it locally and added it as an image. The rendered formula is:
$$
\text{ensemble}(c) = \begin{cases}
1 & \text{if } \sum_{i: p_c^{m_i} \geq 0.5} [\text{confidence}_c^{m_i} \cdot \text{model_weight}_{m_i} \cdot \text{trust}_c^{m_i}] > \sum_{i: p_c^{m_i} < 0.5} [\text{confidence}_c^{m_i} \cdot \text{model_weight}_{m_i} \cdot \text{trust}_c^{m_i}] \\
0 & \text{otherwise}
\end{cases}
$$
-->

Here, confidence is the model's (self-reported) confidence in its prediction, calculated as
$
\text{confidence}_c^{m_i} = 2|p_c^{m_i} - 0.5|
$
For example, if a model makes a positive prediction with $p_c^{m_i} = 0.55$, the confidence is $2|0.55 - 0.5| = 0.1$.
One could say that the model is not very confident in its prediction and very close to switching to a negative prediction.
If another model is very sure about its negative prediction with $p_c^{m_j} = 0.1$, the confidence is $2|0.1 - 0.5| = 0.8$.
Therefore, if in doubt, we are more confident in the negative prediction.

Confidence can be disabled by the `use_confidence` parameter of the predict method (default: True).

The`model_weight` can be set for each model in the configuration file (default: 1). This is used to favor a certain
model independently of a given class.
`Trust` is based on the model's performance on a validation set. After training, we evaluate the Machine Learning models
on a validation set for each class. If the `ensemble_type` is set to `wmv-f1`, the trust is calculated as F1-score $^{6.25}$.
If the `ensemble_type` is set to `mv` (the default), the trust is set to 1 for all models.

### Inconsistency resolution
After a decision has been made for each class independently, the consistency of the predictions with regard to the ChEBI hierarchy
and disjointness axioms is checked. This is
done in 3 steps:
- (1) First, the hierarchy is corrected. For each pair of classes $A$ and $B$ where $A$ is a subclass of $B$ (following
the is-a relation in ChEBI), we set the ensemble prediction of $A$ to $0$ if the _absolute value_ of $B$'s score is large than that of $A$. For example, if $A$ has a net score of $3$ and $B$ has a net score of $-4$, the ensemble will set $A$ to $0$ (i.e., predict neither $A$ nor $B$).
- (2) Next, we check for disjointness. This is not specified directly in ChEBI, but in an additional ChEBI module ([chebi-disjoints.owl](https://ftp.ebi.ac.uk/pub/databases/chebi/ontology/)).
We have extracted these disjointness axioms into a CSV file and added some more disjointness axioms ourselves (see
`data>disjoint_chebi.csv` and `data>disjoint_additional.csv`). If two classes $A$ and $B$ are disjoint and we predict
both, we select one with the higher class score and set the other to 0.
- (3) Since the second step might have introduced new inconsistencies into the hierarchy, we repeat the first step, but
with a small change. For a pair of classes $A \subseteq B$ with predictions $1$ and $0$, instead of setting $B$ to $1$,
we now set $A$ to $0$. This has the advantage that we cannot introduce new disjointness-inconsistencies and don't have
to repeat step 2.
