msfiddle 2.0.1__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- msfiddle-2.2.0/PKG-INFO +169 -0
- msfiddle-2.2.0/README.md +128 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/__init__.py +6 -1
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/api.py +17 -4
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/config/fiddle_tcn_orbitrap.yml +12 -7
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/config/fiddle_tcn_qtof.yml +14 -7
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/dataset.py +32 -6
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/download.py +17 -6
- msfiddle-2.2.0/msfiddle/external_results.py +380 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/main.py +17 -2
- msfiddle-2.2.0/msfiddle/optim.py +160 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/utils/msms_utils.py +134 -103
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/utils/pkl_utils.py +140 -36
- msfiddle-2.2.0/msfiddle/utils/refine_utils.py +712 -0
- msfiddle-2.2.0/msfiddle.egg-info/PKG-INFO +169 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle.egg-info/SOURCES.txt +8 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/setup.py +3 -3
- {msfiddle-2.0.1 → msfiddle-2.2.0}/tests/test_api.py +101 -1
- msfiddle-2.2.0/tests/test_download.py +29 -0
- msfiddle-2.2.0/tests/test_external_results.py +282 -0
- msfiddle-2.2.0/tests/test_generate_ms.py +52 -0
- msfiddle-2.2.0/tests/test_muon.py +94 -0
- msfiddle-2.2.0/tests/test_preprocessing_speedups.py +148 -0
- msfiddle-2.2.0/tests/test_refine_enumeration.py +136 -0
- msfiddle-2.2.0/tests/test_refine_fast_mass.py +106 -0
- msfiddle-2.0.1/PKG-INFO +0 -251
- msfiddle-2.0.1/README.md +0 -210
- msfiddle-2.0.1/msfiddle/utils/refine_utils.py +0 -376
- msfiddle-2.0.1/msfiddle.egg-info/PKG-INFO +0 -251
- msfiddle-2.0.1/tests/test_download.py +0 -15
- {msfiddle-2.0.1 → msfiddle-2.2.0}/LICENSE +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/config/fiddle_tcn_casmi.yml +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/config/fiddle_tcn_demo.yml +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/config/fiddle_tcn_demo_wo_da.yml +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/config/fiddle_tcn_embl.yml +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/demo/input_msms.mgf +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/demo/output_buddy.csv +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/demo/output_fiddle.csv +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/demo/output_fiddle_all.csv +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/demo/output_sirius.csv +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/model_tcn.py +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/utils/__init__.py +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle/utils/mol_utils.py +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle.egg-info/dependency_links.txt +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle.egg-info/entry_points.txt +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle.egg-info/requires.txt +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/msfiddle.egg-info/top_level.txt +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/setup.cfg +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/tests/test_main.py +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/tests/test_mol_utils.py +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/tests/test_msms_utils.py +0 -0
- {msfiddle-2.0.1 → msfiddle-2.2.0}/tests/test_refine_utils.py +0 -0
msfiddle-2.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: msfiddle
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: A package for predicting chemical formulas from tandem mass spectra
|
|
5
|
+
Home-page: https://github.com/JosieHong/FIDDLE
|
|
6
|
+
Author: Yuhui Hong
|
|
7
|
+
Author-email: josieexception@outlook.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy<2.0.0,>=1.20.0
|
|
19
|
+
Requires-Dist: pandas>=2.0.0
|
|
20
|
+
Requires-Dist: tqdm>=4.60.0
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Requires-Dist: scikit-learn>=1.0.0
|
|
23
|
+
Requires-Dist: scipy>=1.8.0
|
|
24
|
+
Requires-Dist: pyarrow>=10.0.0
|
|
25
|
+
Requires-Dist: rdkit>=2022.03.5
|
|
26
|
+
Requires-Dist: molmass
|
|
27
|
+
Requires-Dist: pyteomics
|
|
28
|
+
Provides-Extra: inference
|
|
29
|
+
Requires-Dist: torch; extra == "inference"
|
|
30
|
+
Dynamic: author
|
|
31
|
+
Dynamic: author-email
|
|
32
|
+
Dynamic: classifier
|
|
33
|
+
Dynamic: description
|
|
34
|
+
Dynamic: description-content-type
|
|
35
|
+
Dynamic: home-page
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
Dynamic: provides-extra
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
41
|
+
|
|
42
|
+
# FIDDLE
|
|
43
|
+
|
|
44
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
45
|
+
[](https://doi.org/10.5281/zenodo.19181279)
|
|
46
|
+
[](https://github.com/JosieHong/FIDDLE/releases)
|
|
47
|
+
|
|
48
|
+
[<img src="https://raw.githubusercontent.com/JosieHong/FIDDLE/main/img/fiddle_logo.png" align="right" width="220">](https://github.com/JosieHong/FIDDLE)
|
|
49
|
+
|
|
50
|
+
FIDDLE is a deep learning method for predicting molecular formulas from MS/MS spectra. This repository contains the full research codebase for model training, evaluation, and paper reproduction, plus the [`msfiddle`](https://pypi.org/project/msfiddle/) PyPI package (in [`./msfiddle/`](https://github.com/JosieHong/FIDDLE/tree/main/msfiddle)) for inference-only use.
|
|
51
|
+
|
|
52
|
+
- **Paper:** [Nature Communications (2025)](https://www.nature.com/articles/s41467-025-66060-9)
|
|
53
|
+
- **CLI and Python API:** `pip install "msfiddle[inference]"` — see the [package docs](https://msfiddle.readthedocs.io)
|
|
54
|
+
- **Try this demo!** [FIDDLE on Hugging Face](https://huggingface.co/spaces/J0siee/FIDDLE)
|
|
55
|
+
|
|
56
|
+
<br clear="right"/>
|
|
57
|
+
|
|
58
|
+
> **Breaking change (v2.0.0):** The rescore model has been redesigned (Siamese architecture), see details in [CHANGELOG.md](https://github.com/JosieHong/FIDDLE/blob/main/CHANGELOG.md).
|
|
59
|
+
|
|
60
|
+
## Set up
|
|
61
|
+
|
|
62
|
+
### Requirements
|
|
63
|
+
|
|
64
|
+
1. Install [Anaconda](https://docs.anaconda.com/anaconda/install/), if not already installed.
|
|
65
|
+
|
|
66
|
+
2. Create the environment with the necessary packages:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
conda env create -f environment.yml
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
On Apple Silicon (macOS ARM64), use `environment_mac.yml` instead.
|
|
73
|
+
|
|
74
|
+
3. (optional) Install [BUDDY](https://github.com/Philipbear/msbuddy) and [SIRIUS](https://v6.docs.sirius-ms.io/) following the respective installation instructions provided in each tool's documentation.
|
|
75
|
+
|
|
76
|
+
### Pre-trained Model Weights
|
|
77
|
+
|
|
78
|
+
To use the pre-trained models, please use the following scripts to download the weights from the [release page](https://github.com/JosieHong/FIDDLE/releases/tag/v2.2.0) and place them in the `./check_point/` directory:
|
|
79
|
+
|
|
80
|
+
- **Orbitrap models**:
|
|
81
|
+
- `fiddle_tcn_orbitrap.pt`: formula prediction model on Orbitrap spectra
|
|
82
|
+
- `fiddle_rescore_orbitrap.pt`: rescore model on Orbitrap spectra
|
|
83
|
+
- **Q-TOF models**:
|
|
84
|
+
- `fiddle_tcn_qtof.pt`: formula prediction model on Q-TOF spectra
|
|
85
|
+
- `fiddle_rescore_qtof.pt`: rescore model on Q-TOF spectra
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
bash ./running_scripts/download_models.sh
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Usage
|
|
92
|
+
|
|
93
|
+
The input format is `mgf`, where `title`, `precursor_mz`, `precursor_type`, `collision_energy` fields are required. Here, we sampled 21 spectra from the EMBL-MCF 2.0 dataset as an example.
|
|
94
|
+
|
|
95
|
+
```mgf
|
|
96
|
+
BEGIN IONS
|
|
97
|
+
TITLE=EMBL_MCF_2_0_HRMS_Library000531
|
|
98
|
+
PEPMASS=129.01941
|
|
99
|
+
CHARGE=1-
|
|
100
|
+
PRECURSOR_TYPE=[M-H]-
|
|
101
|
+
PRECURSOR_MZ=129.01941
|
|
102
|
+
COLLISION_ENERGY=50.0
|
|
103
|
+
SMILES=[H]OC(=O)C([H])=C(C(=O)O[H])C([H])([H])[H]
|
|
104
|
+
FORMULA=C5H6O4
|
|
105
|
+
THEORETICAL_PRECURSOR_MZ=129.018785
|
|
106
|
+
PPM=4.844255818912111
|
|
107
|
+
SIMULATED_PRECURSOR_MZ=129.02032113281717
|
|
108
|
+
41.2041 0.410228
|
|
109
|
+
55.7698 0.503672
|
|
110
|
+
56.8647 0.461943
|
|
111
|
+
85.0296 100.0
|
|
112
|
+
129.0196 8.036902
|
|
113
|
+
END IONS
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Run FIDDLE!**
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
python scripts/run_fiddle.py --test_data ./msfiddle/demo/input_msms.mgf \
|
|
120
|
+
--config_path ./msfiddle/config/fiddle_tcn_orbitrap.yml \
|
|
121
|
+
--resume_path ./check_point/fiddle_tcn_orbitrap.pt \
|
|
122
|
+
--rescore_resume_path ./check_point/fiddle_rescore_orbitrap.pt \
|
|
123
|
+
--result_path ./msfiddle/demo/output_fiddle.csv --device 0
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If you'd like to integrate the results from SIRIUS and BUDDY, please organize the results in the format shown in `./msfiddle/demo/buddy_output.csv` and `./msfiddle/demo/sirius_output.csv`, and provide them to run FIDDLE:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
python scripts/run_fiddle.py --test_data ./msfiddle/demo/input_msms.mgf \
|
|
130
|
+
--config_path ./msfiddle/config/fiddle_tcn_orbitrap.yml \
|
|
131
|
+
--resume_path ./check_point/fiddle_tcn_orbitrap.pt \
|
|
132
|
+
--rescore_resume_path ./check_point/fiddle_rescore_orbitrap.pt \
|
|
133
|
+
--buddy_path ./msfiddle/demo/output_buddy.csv \
|
|
134
|
+
--sirius_path ./msfiddle/demo/output_sirius.csv \
|
|
135
|
+
--result_path ./msfiddle/demo/output_fiddle_all.csv --device 0
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See [`scripts/test_caffeine.py`](https://github.com/JosieHong/FIDDLE/blob/main/scripts/test_caffeine.py) for a worked example running FIDDLE on a caffeine Orbitrap spectrum fetched live from GNPS.
|
|
139
|
+
|
|
140
|
+
## Reproduce paper results
|
|
141
|
+
|
|
142
|
+
All scripts should be run from the repository root (`FIDDLE/`).
|
|
143
|
+
|
|
144
|
+
| Script | Description |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `running_scripts/experiments_test_benchmark.sh` | Evaluate on external benchmarks (CASMI 2016, CASMI 2017, EMBL-MCF 2.0) |
|
|
147
|
+
| `running_scripts/experiments_test_nist23.sh` | Evaluate on NIST23 |
|
|
148
|
+
| `running_scripts/experiments_test_chimeric.sh` | Evaluate on chimeric spectra |
|
|
149
|
+
| `running_scripts/experiments_test_noised.sh` | Evaluate under noise conditions |
|
|
150
|
+
| `running_scripts/experiments_ablation_study.sh` | Run ablation study |
|
|
151
|
+
| `running_scripts/experiments_demo.sh` | Run demo experiment |
|
|
152
|
+
| `running_scripts/train_released_models.sh` | Train TCN and rescore models for both Orbitrap and Q-TOF |
|
|
153
|
+
|
|
154
|
+
For training from scratch, see the train scripts (`scripts/train_tcn_gpus.py`, `scripts/train_tcn_gpus_cl.py`, `scripts/train_rescore.py`) and the corresponding config files in `./msfiddle/config/`.
|
|
155
|
+
|
|
156
|
+
## Citation
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
@article{hong2025fiddle,
|
|
160
|
+
title={FIDDLE: a deep learning method for chemical formulas prediction from tandem mass spectra},
|
|
161
|
+
author={Hong, Yuhui and Li, Sujun and Ye, Yuzhen and Tang, Haixu},
|
|
162
|
+
journal={Nature Communications},
|
|
163
|
+
volume={16},
|
|
164
|
+
number={1},
|
|
165
|
+
pages={11102},
|
|
166
|
+
year={2025},
|
|
167
|
+
publisher={Nature Publishing Group UK London}
|
|
168
|
+
}
|
|
169
|
+
```
|
msfiddle-2.2.0/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# FIDDLE
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
4
|
+
[](https://doi.org/10.5281/zenodo.19181279)
|
|
5
|
+
[](https://github.com/JosieHong/FIDDLE/releases)
|
|
6
|
+
|
|
7
|
+
[<img src="https://raw.githubusercontent.com/JosieHong/FIDDLE/main/img/fiddle_logo.png" align="right" width="220">](https://github.com/JosieHong/FIDDLE)
|
|
8
|
+
|
|
9
|
+
FIDDLE is a deep learning method for predicting molecular formulas from MS/MS spectra. This repository contains the full research codebase for model training, evaluation, and paper reproduction, plus the [`msfiddle`](https://pypi.org/project/msfiddle/) PyPI package (in [`./msfiddle/`](https://github.com/JosieHong/FIDDLE/tree/main/msfiddle)) for inference-only use.
|
|
10
|
+
|
|
11
|
+
- **Paper:** [Nature Communications (2025)](https://www.nature.com/articles/s41467-025-66060-9)
|
|
12
|
+
- **CLI and Python API:** `pip install "msfiddle[inference]"` — see the [package docs](https://msfiddle.readthedocs.io)
|
|
13
|
+
- **Try this demo!** [FIDDLE on Hugging Face](https://huggingface.co/spaces/J0siee/FIDDLE)
|
|
14
|
+
|
|
15
|
+
<br clear="right"/>
|
|
16
|
+
|
|
17
|
+
> **Breaking change (v2.0.0):** The rescore model has been redesigned (Siamese architecture), see details in [CHANGELOG.md](https://github.com/JosieHong/FIDDLE/blob/main/CHANGELOG.md).
|
|
18
|
+
|
|
19
|
+
## Set up
|
|
20
|
+
|
|
21
|
+
### Requirements
|
|
22
|
+
|
|
23
|
+
1. Install [Anaconda](https://docs.anaconda.com/anaconda/install/), if not already installed.
|
|
24
|
+
|
|
25
|
+
2. Create the environment with the necessary packages:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
conda env create -f environment.yml
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
On Apple Silicon (macOS ARM64), use `environment_mac.yml` instead.
|
|
32
|
+
|
|
33
|
+
3. (optional) Install [BUDDY](https://github.com/Philipbear/msbuddy) and [SIRIUS](https://v6.docs.sirius-ms.io/) following the respective installation instructions provided in each tool's documentation.
|
|
34
|
+
|
|
35
|
+
### Pre-trained Model Weights
|
|
36
|
+
|
|
37
|
+
To use the pre-trained models, please use the following scripts to download the weights from the [release page](https://github.com/JosieHong/FIDDLE/releases/tag/v2.2.0) and place them in the `./check_point/` directory:
|
|
38
|
+
|
|
39
|
+
- **Orbitrap models**:
|
|
40
|
+
- `fiddle_tcn_orbitrap.pt`: formula prediction model on Orbitrap spectra
|
|
41
|
+
- `fiddle_rescore_orbitrap.pt`: rescore model on Orbitrap spectra
|
|
42
|
+
- **Q-TOF models**:
|
|
43
|
+
- `fiddle_tcn_qtof.pt`: formula prediction model on Q-TOF spectra
|
|
44
|
+
- `fiddle_rescore_qtof.pt`: rescore model on Q-TOF spectra
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bash ./running_scripts/download_models.sh
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
The input format is `mgf`, where `title`, `precursor_mz`, `precursor_type`, `collision_energy` fields are required. Here, we sampled 21 spectra from the EMBL-MCF 2.0 dataset as an example.
|
|
53
|
+
|
|
54
|
+
```mgf
|
|
55
|
+
BEGIN IONS
|
|
56
|
+
TITLE=EMBL_MCF_2_0_HRMS_Library000531
|
|
57
|
+
PEPMASS=129.01941
|
|
58
|
+
CHARGE=1-
|
|
59
|
+
PRECURSOR_TYPE=[M-H]-
|
|
60
|
+
PRECURSOR_MZ=129.01941
|
|
61
|
+
COLLISION_ENERGY=50.0
|
|
62
|
+
SMILES=[H]OC(=O)C([H])=C(C(=O)O[H])C([H])([H])[H]
|
|
63
|
+
FORMULA=C5H6O4
|
|
64
|
+
THEORETICAL_PRECURSOR_MZ=129.018785
|
|
65
|
+
PPM=4.844255818912111
|
|
66
|
+
SIMULATED_PRECURSOR_MZ=129.02032113281717
|
|
67
|
+
41.2041 0.410228
|
|
68
|
+
55.7698 0.503672
|
|
69
|
+
56.8647 0.461943
|
|
70
|
+
85.0296 100.0
|
|
71
|
+
129.0196 8.036902
|
|
72
|
+
END IONS
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Run FIDDLE!**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python scripts/run_fiddle.py --test_data ./msfiddle/demo/input_msms.mgf \
|
|
79
|
+
--config_path ./msfiddle/config/fiddle_tcn_orbitrap.yml \
|
|
80
|
+
--resume_path ./check_point/fiddle_tcn_orbitrap.pt \
|
|
81
|
+
--rescore_resume_path ./check_point/fiddle_rescore_orbitrap.pt \
|
|
82
|
+
--result_path ./msfiddle/demo/output_fiddle.csv --device 0
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
If you'd like to integrate the results from SIRIUS and BUDDY, please organize the results in the format shown in `./msfiddle/demo/buddy_output.csv` and `./msfiddle/demo/sirius_output.csv`, and provide them to run FIDDLE:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
python scripts/run_fiddle.py --test_data ./msfiddle/demo/input_msms.mgf \
|
|
89
|
+
--config_path ./msfiddle/config/fiddle_tcn_orbitrap.yml \
|
|
90
|
+
--resume_path ./check_point/fiddle_tcn_orbitrap.pt \
|
|
91
|
+
--rescore_resume_path ./check_point/fiddle_rescore_orbitrap.pt \
|
|
92
|
+
--buddy_path ./msfiddle/demo/output_buddy.csv \
|
|
93
|
+
--sirius_path ./msfiddle/demo/output_sirius.csv \
|
|
94
|
+
--result_path ./msfiddle/demo/output_fiddle_all.csv --device 0
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
See [`scripts/test_caffeine.py`](https://github.com/JosieHong/FIDDLE/blob/main/scripts/test_caffeine.py) for a worked example running FIDDLE on a caffeine Orbitrap spectrum fetched live from GNPS.
|
|
98
|
+
|
|
99
|
+
## Reproduce paper results
|
|
100
|
+
|
|
101
|
+
All scripts should be run from the repository root (`FIDDLE/`).
|
|
102
|
+
|
|
103
|
+
| Script | Description |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `running_scripts/experiments_test_benchmark.sh` | Evaluate on external benchmarks (CASMI 2016, CASMI 2017, EMBL-MCF 2.0) |
|
|
106
|
+
| `running_scripts/experiments_test_nist23.sh` | Evaluate on NIST23 |
|
|
107
|
+
| `running_scripts/experiments_test_chimeric.sh` | Evaluate on chimeric spectra |
|
|
108
|
+
| `running_scripts/experiments_test_noised.sh` | Evaluate under noise conditions |
|
|
109
|
+
| `running_scripts/experiments_ablation_study.sh` | Run ablation study |
|
|
110
|
+
| `running_scripts/experiments_demo.sh` | Run demo experiment |
|
|
111
|
+
| `running_scripts/train_released_models.sh` | Train TCN and rescore models for both Orbitrap and Q-TOF |
|
|
112
|
+
|
|
113
|
+
For training from scratch, see the train scripts (`scripts/train_tcn_gpus.py`, `scripts/train_tcn_gpus_cl.py`, `scripts/train_rescore.py`) and the corresponding config files in `./msfiddle/config/`.
|
|
114
|
+
|
|
115
|
+
## Citation
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
@article{hong2025fiddle,
|
|
119
|
+
title={FIDDLE: a deep learning method for chemical formulas prediction from tandem mass spectra},
|
|
120
|
+
author={Hong, Yuhui and Li, Sujun and Ye, Yuzhen and Tang, Haixu},
|
|
121
|
+
journal={Nature Communications},
|
|
122
|
+
volume={16},
|
|
123
|
+
number={1},
|
|
124
|
+
pages={11102},
|
|
125
|
+
year={2025},
|
|
126
|
+
publisher={Nature Publishing Group UK London}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
msfiddle: A package for predicting chemical formulas from tandem mass spectra
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
from importlib.metadata import PackageNotFoundError, version as _pkg_version
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
__version__ = _pkg_version("msfiddle")
|
|
9
|
+
except PackageNotFoundError: # running from a source checkout without install
|
|
10
|
+
__version__ = "0.0.0+unknown"
|
|
6
11
|
|
|
7
12
|
from .api import (
|
|
8
13
|
MsFiddlePredictor,
|
|
@@ -20,6 +20,7 @@ from pyteomics import mgf
|
|
|
20
20
|
|
|
21
21
|
from .download import download_models as download_pretrained_models
|
|
22
22
|
from .download import get_checkpoint_dir
|
|
23
|
+
from .external_results import load_buddy_results, load_sirius_results
|
|
23
24
|
from .utils.mol_utils import (
|
|
24
25
|
ATOMS_INDEX,
|
|
25
26
|
formula_to_dict,
|
|
@@ -198,6 +199,10 @@ class MsFiddlePredictor:
|
|
|
198
199
|
) -> pd.DataFrame:
|
|
199
200
|
"""Predict formulas for spectra in an MGF file.
|
|
200
201
|
|
|
202
|
+
``buddy_path`` and ``sirius_path`` may point to either native/original
|
|
203
|
+
BUDDY/msbuddy and SIRIUS formula-identification outputs or legacy
|
|
204
|
+
msfiddle-normalized CSV files.
|
|
205
|
+
|
|
201
206
|
The returned DataFrame uses the same result columns as the CLI CSV.
|
|
202
207
|
"""
|
|
203
208
|
|
|
@@ -212,8 +217,8 @@ class MsFiddlePredictor:
|
|
|
212
217
|
encoded = self._load_mgf_spectra(test_data_path)
|
|
213
218
|
predictions = self._predict_encoded(encoded, batch_size=batch_size)
|
|
214
219
|
|
|
215
|
-
buddy_df =
|
|
216
|
-
sirius_df =
|
|
220
|
+
buddy_df = load_buddy_results(buddy_path) if buddy_path else None
|
|
221
|
+
sirius_df = load_sirius_results(sirius_path) if sirius_path else None
|
|
217
222
|
|
|
218
223
|
refined_formula = {f"Refined Formula ({k})": [] for k in range(top_k)}
|
|
219
224
|
refined_mass = {f"Refined Mass ({k})": [] for k in range(top_k)}
|
|
@@ -787,7 +792,8 @@ class MsFiddlePredictor:
|
|
|
787
792
|
candidates.extend(
|
|
788
793
|
formula
|
|
789
794
|
for formula, score in zip(formulas, scores)
|
|
790
|
-
if
|
|
795
|
+
if _is_external_formula(formula)
|
|
796
|
+
and pd.notna(score)
|
|
791
797
|
and score < self.config["post_processing"]["buddy_fdr_thr"]
|
|
792
798
|
)
|
|
793
799
|
if sirius_df is not None:
|
|
@@ -815,7 +821,8 @@ class MsFiddlePredictor:
|
|
|
815
821
|
candidates.extend(
|
|
816
822
|
formula
|
|
817
823
|
for formula, score in zip(formulas, scores)
|
|
818
|
-
if
|
|
824
|
+
if _is_external_formula(formula)
|
|
825
|
+
and pd.notna(score)
|
|
819
826
|
and score > self.config["post_processing"]["sirius_score_thr"]
|
|
820
827
|
)
|
|
821
828
|
return candidates
|
|
@@ -934,6 +941,12 @@ def _normalize_instrument_type(instrument_type: str) -> str:
|
|
|
934
941
|
return normalized
|
|
935
942
|
|
|
936
943
|
|
|
944
|
+
def _is_external_formula(value: object) -> bool:
|
|
945
|
+
if pd.isna(value):
|
|
946
|
+
return False
|
|
947
|
+
return str(value).strip().lower() not in {"", "na", "nan", "none", "null", "<na>"}
|
|
948
|
+
|
|
949
|
+
|
|
937
950
|
def _load_config(config_path: Path) -> dict[str, Any]:
|
|
938
951
|
if not config_path.exists():
|
|
939
952
|
raise FileNotFoundError(f"Config file not found: {config_path}")
|
|
@@ -131,12 +131,14 @@ model:
|
|
|
131
131
|
# ------------------------
|
|
132
132
|
train:
|
|
133
133
|
epochs: 500
|
|
134
|
-
batch_size: 512 # 128
|
|
134
|
+
batch_size: 512 # 128 fits 11GB GPUs (CL training holds 2 forward graphs, memory ~ 2x batch)
|
|
135
|
+
grad_accum_steps: 1 # optimizer step every N batches; effective batch = batch_size x N (256 x 2 on 2 x 11GB GPUs = 512)
|
|
135
136
|
num_workers: 8
|
|
136
137
|
lr: 0.001
|
|
137
138
|
weight_decay: 0.01
|
|
138
139
|
warmup_ratio: 0.1
|
|
139
|
-
early_stop_step:
|
|
140
|
+
early_stop_step: 15 # ~3x plateau patience so >=2 LR halvings get a chance
|
|
141
|
+
early_stop_min_epochs: 5 # patience does not count before this epoch
|
|
140
142
|
patience: 5
|
|
141
143
|
noised_times: 0
|
|
142
144
|
|
|
@@ -150,15 +152,18 @@ train_rescore:
|
|
|
150
152
|
# ------------------------
|
|
151
153
|
# postprocessing settings
|
|
152
154
|
# ------------------------
|
|
153
|
-
post_processing:
|
|
155
|
+
post_processing:
|
|
154
156
|
# Orbitrap
|
|
155
|
-
|
|
156
|
-
|
|
157
|
+
# D0c: halogens in the refinement search so a missed halogen can be introduced and
|
|
158
|
+
# H rebalanced to the exact precursor mass. (Orbitrap is halogen-rich, so the model
|
|
159
|
+
# usually predicts halogens already; this mainly helps the cases it misses.)
|
|
160
|
+
refine_atom_type: ['C', 'O', 'N', 'H', 'Cl', 'Br', 'F', 'I', 'S', 'P']
|
|
161
|
+
refine_atom_num: [5, 5, 5, -1, 2, 1, 2, 1, 2, 1] # config E caps (08/2026 sweep): recall@20 88.5% -> 94.3% at 10ppm, 95.7% if 5ppm window validates
|
|
157
162
|
|
|
158
|
-
mass_tolerance:
|
|
163
|
+
mass_tolerance: 5.0 # config E (08/2026 sweep): matches the data filters' ppm_tolerance 5 (no admitted spectrum can be orphaned) and Orbitrap instrument accuracy; halves the decoy count vs 10 ppm, recall@20 ceiling 95.7%
|
|
159
164
|
ppm_mode: True
|
|
160
165
|
time_out: 300
|
|
161
166
|
maxium_miss_atom_num: 20 # search space
|
|
162
|
-
top_k:
|
|
167
|
+
top_k: 20 # config E (08/2026 sweep): +2 points of rescorer ceiling over K=10; keep pair-generation and inference matched
|
|
163
168
|
buddy_fdr_thr: 0.6
|
|
164
169
|
sirius_score_thr: -5
|
|
@@ -165,17 +165,19 @@ model:
|
|
|
165
165
|
# ------------------------
|
|
166
166
|
train:
|
|
167
167
|
epochs: 200
|
|
168
|
-
batch_size: 512 # 128
|
|
168
|
+
batch_size: 512 # 128 fits 11GB GPUs (CL training holds 2 forward graphs, memory ~ 2x batch)
|
|
169
|
+
grad_accum_steps: 1 # optimizer step every N batches; effective batch = batch_size x N (256 x 2 on 2 x 11GB GPUs = 512)
|
|
169
170
|
num_workers: 8
|
|
170
171
|
lr: 0.002 # 0.001
|
|
171
172
|
weight_decay: 0.01
|
|
172
173
|
warmup_ratio: 0.1
|
|
173
|
-
early_stop_step:
|
|
174
|
+
early_stop_step: 15 # ~3x plateau patience so >=2 LR halvings get a chance
|
|
175
|
+
early_stop_min_epochs: 5 # patience does not count before this epoch
|
|
174
176
|
patience: 5
|
|
175
177
|
noised_times: 3
|
|
176
178
|
|
|
177
179
|
train_rescore:
|
|
178
|
-
epochs:
|
|
180
|
+
epochs: 20
|
|
179
181
|
batch_size: 128
|
|
180
182
|
num_workers: 8
|
|
181
183
|
lr: 0.0001
|
|
@@ -184,14 +186,19 @@ train_rescore:
|
|
|
184
186
|
# ------------------------
|
|
185
187
|
# postprocessing settings
|
|
186
188
|
# ------------------------
|
|
187
|
-
post_processing:
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
post_processing:
|
|
190
|
+
# D0c (tuned): let refinement INTRODUCE a missed halogen and rebalance H to the
|
|
191
|
+
# exact precursor mass. Base was ['C','O','N','H'] -> halogens unreachable.
|
|
192
|
+
# S/P deliberately NOT in base (already reachable when the model predicts them;
|
|
193
|
+
# adding them only created spurious candidates). Halogen caps kept tight to limit
|
|
194
|
+
# near-isobaric false positives on the CONH majority.
|
|
195
|
+
refine_atom_type: ['C', 'O', 'N', 'H', 'Cl', 'Br', 'F', 'I', 'S', 'P']
|
|
196
|
+
refine_atom_num: [5, 5, 5, -1, 2, 1, 2, 1, 2, 1] # config B (08/2026 sweep): S/P reachable; recall@20 92.3% -> 94.0%
|
|
190
197
|
|
|
191
198
|
mass_tolerance: 10.0 # target space
|
|
192
199
|
ppm_mode: True
|
|
193
200
|
time_out: -1 # do not limit the time
|
|
194
201
|
maxium_miss_atom_num: 50 # search space
|
|
195
|
-
top_k:
|
|
202
|
+
top_k: 20 # config B (08/2026 sweep): +2 points of rescorer ceiling over K=10; keep pair-generation and inference matched
|
|
196
203
|
buddy_fdr_thr: 0.6
|
|
197
204
|
sirius_score_thr: -5
|
|
@@ -55,7 +55,7 @@ class MS2FDataset_CL(Dataset):
|
|
|
55
55
|
for data_item in self.data:
|
|
56
56
|
spec = data_item["spec"]
|
|
57
57
|
data_item["spec"] = np.concatenate(
|
|
58
|
-
(spec, np.zeros((padding_dim, spec.shape[1]))), axis=0
|
|
58
|
+
(spec, np.zeros((padding_dim, spec.shape[1]), dtype=spec.dtype)), axis=0
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
def augment_with_noise(self, data, pairs, noised_times):
|
|
@@ -76,11 +76,16 @@ class MS2FDataset_CL(Dataset):
|
|
|
76
76
|
"""
|
|
77
77
|
original_length = len(data)
|
|
78
78
|
augmented_data = copy.deepcopy(data)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
# Copies are appended in whole-dataset blocks (round t of every
|
|
80
|
+
# spectrum, then round t+1, ...) so that augmented copy t of item j
|
|
81
|
+
# sits at index original_length * (t + 1) + j — the layout the pair
|
|
82
|
+
# index shift below assumes. Interleaving copies per spectrum instead
|
|
83
|
+
# silently pairs unrelated molecules once noised_times >= 2.
|
|
84
|
+
for _ in range(noised_times):
|
|
85
|
+
for data_item in tqdm(data, desc="Data Augmentation"):
|
|
86
|
+
spec = data_item["spec"][:, 0]
|
|
87
|
+
spec_mask = np.where(spec > 0, 1.0, 0.0)
|
|
82
88
|
|
|
83
|
-
for _ in range(noised_times):
|
|
84
89
|
noise = np.random.normal(0, 0.1, len(spec))
|
|
85
90
|
new_spec = spec + noise * spec_mask
|
|
86
91
|
|
|
@@ -151,7 +156,7 @@ class MS2FDataset(Dataset):
|
|
|
151
156
|
for data_item in self.data:
|
|
152
157
|
spec = data_item["spec"]
|
|
153
158
|
data_item["spec"] = np.concatenate(
|
|
154
|
-
(spec, np.zeros((padding_dim, spec.shape[1]))), axis=0
|
|
159
|
+
(spec, np.zeros((padding_dim, spec.shape[1]), dtype=spec.dtype)), axis=0
|
|
155
160
|
)
|
|
156
161
|
|
|
157
162
|
def augment_with_noise(self, data, noised_times):
|
|
@@ -293,6 +298,18 @@ class MGFDataset(Dataset):
|
|
|
293
298
|
'precursor_type', and 'use_simulated_precursor_mz'.
|
|
294
299
|
"""
|
|
295
300
|
for spectrum in spectra:
|
|
301
|
+
# Skip (not crash on) adducts the encoder does not support: user
|
|
302
|
+
# MGFs routinely contain a few, and one unsupported PRECURSOR_TYPE
|
|
303
|
+
# must not abort the whole run with a KeyError.
|
|
304
|
+
precursor_type = spectrum["params"]["precursor_type"]
|
|
305
|
+
if precursor_type not in encoder["type2charge"]:
|
|
306
|
+
print(
|
|
307
|
+
"Skip {}: unsupported precursor type {}".format(
|
|
308
|
+
spectrum["params"].get("title", "?"), precursor_type
|
|
309
|
+
)
|
|
310
|
+
)
|
|
311
|
+
continue
|
|
312
|
+
|
|
296
313
|
good_spec, _, _, spec_arr = generate_ms(
|
|
297
314
|
x=spectrum["m/z array"],
|
|
298
315
|
y=spectrum["intensity array"],
|
|
@@ -309,6 +326,15 @@ class MGFDataset(Dataset):
|
|
|
309
326
|
adjust_neutral_add_vec, adjust_precursor_type = self.melt_neutral_precursor(
|
|
310
327
|
spectrum["params"]["precursor_type"]
|
|
311
328
|
)
|
|
329
|
+
if adjust_precursor_type not in encoder["precursor_type"]:
|
|
330
|
+
print(
|
|
331
|
+
"Skip {}: unsupported precursor type {} (from {})".format(
|
|
332
|
+
spectrum["params"].get("title", "?"),
|
|
333
|
+
adjust_precursor_type,
|
|
334
|
+
precursor_type,
|
|
335
|
+
)
|
|
336
|
+
)
|
|
337
|
+
continue
|
|
312
338
|
|
|
313
339
|
ce, nce = parse_collision_energy(
|
|
314
340
|
ce_str=spectrum["params"]["collision_energy"],
|
|
@@ -16,11 +16,18 @@ from importlib.metadata import (
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def checkpoint_release_for_package_version(package_version):
|
|
19
|
-
"""Return the
|
|
20
|
-
|
|
19
|
+
"""Return the FIDDLE release tag that holds this version's checkpoints.
|
|
20
|
+
|
|
21
|
+
From 2.2 on, each minor version has its own checkpoints (tag vX.Y.0,
|
|
22
|
+
shared by its patch releases); 2.0 and 2.1 share the v2.0.0 assets.
|
|
23
|
+
"""
|
|
24
|
+
match = re.match(r"^(\d+)(?:\.(\d+))?", str(package_version))
|
|
21
25
|
if match is None:
|
|
22
26
|
return f"v{package_version}"
|
|
23
|
-
|
|
27
|
+
major, minor = int(match.group(1)), int(match.group(2) or 0)
|
|
28
|
+
if (major, minor) < (2, 2):
|
|
29
|
+
return f"v{major}.0.0"
|
|
30
|
+
return f"v{major}.{minor}.0"
|
|
24
31
|
|
|
25
32
|
|
|
26
33
|
try:
|
|
@@ -56,10 +63,14 @@ def download_url(url, output_path):
|
|
|
56
63
|
def get_checkpoint_dir():
|
|
57
64
|
"""Get the directory where model checkpoints are stored.
|
|
58
65
|
|
|
59
|
-
Returns ``~/.msfiddle/check_point
|
|
60
|
-
consistent regardless of working directory or install method.
|
|
66
|
+
Returns ``~/.msfiddle/check_point/<release tag>``, a user-level directory
|
|
67
|
+
that is consistent regardless of working directory or install method. The
|
|
68
|
+
tag keeps checkpoints from different releases apart, so an upgrade never
|
|
69
|
+
silently reuses an older release's weights.
|
|
61
70
|
"""
|
|
62
|
-
return os.path.join(
|
|
71
|
+
return os.path.join(
|
|
72
|
+
os.path.expanduser("~"), ".msfiddle", "check_point", FIDDLE_RELEASE
|
|
73
|
+
)
|
|
63
74
|
|
|
64
75
|
|
|
65
76
|
def download_models(destination=None, models=None):
|