IsoSpecPy 2.4.0__tar.gz → 2.6.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.
Files changed (75) hide show
  1. isospecpy-2.6.0/PKG-INFO +269 -0
  2. isospecpy-2.6.0/README.md +239 -0
  3. isospecpy-2.6.0/VERSION +1 -0
  4. {isospecpy-2.4.0 → isospecpy-2.6.0}/pyproject.toml +19 -2
  5. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/aligned_ptr.h +77 -11
  6. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/allocator.h +8 -1
  7. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/btrd.h +4 -4
  8. isospecpy-2.6.0/src/IsoSpec++/composition.cpp +85 -0
  9. isospecpy-2.6.0/src/IsoSpec++/composition.h +135 -0
  10. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/cwrapper.cpp +222 -0
  11. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/cwrapper.h +68 -1
  12. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/dirtyAllocator.h +2 -1
  13. isospecpy-2.6.0/src/IsoSpec++/element_lookup.cpp +100 -0
  14. isospecpy-2.6.0/src/IsoSpec++/element_lookup.h +36 -0
  15. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/element_tables.cpp +10 -0
  16. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/element_tables.h +1 -1
  17. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/fasta.cpp +28 -1
  18. isospecpy-2.6.0/src/IsoSpec++/fasta.h +120 -0
  19. isospecpy-2.6.0/src/IsoSpec++/fasta_mods.cpp +276 -0
  20. isospecpy-2.6.0/src/IsoSpec++/fasta_mods.h +77 -0
  21. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/fixedEnvelopes.cpp +638 -239
  22. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/fixedEnvelopes.h +25 -9
  23. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isa_dispatch.cpp +90 -32
  24. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isa_kernel_impl.h +17 -6
  25. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isa_kernels.h +11 -2
  26. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isoMath.cpp +71 -13
  27. isospecpy-2.6.0/src/IsoSpec++/isoMath.h +189 -0
  28. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isoSpec++.cpp +302 -144
  29. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isoSpec++.h +250 -34
  30. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/marginalTrek++.cpp +142 -54
  31. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/marginalTrek++.h +37 -26
  32. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/misc.cpp +2 -7
  33. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/misc.h +4 -1
  34. isospecpy-2.6.0/src/IsoSpec++/operators.h +64 -0
  35. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/platform.h +15 -1
  36. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/pod_vector.h +3 -4
  37. isospecpy-2.4.0/src/IsoSpec++/operators.cpp → isospecpy-2.6.0/src/IsoSpec++/python-build.cpp +17 -18
  38. isospecpy-2.6.0/src/IsoSpec++/unimod.cpp +179 -0
  39. isospecpy-2.6.0/src/IsoSpec++/unimod.h +79 -0
  40. isospecpy-2.6.0/src/IsoSpec++/unimod_support.h +2169 -0
  41. isospecpy-2.6.0/src/IsoSpec++/unimod_table_data.h +1027 -0
  42. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/unity-build.cpp +4 -1
  43. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/IsoSpecPy.py +320 -76
  44. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/PeriodicTbl.py +17 -0
  45. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/approximations.py +2 -2
  46. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/isoFFI.py +55 -16
  47. isospecpy-2.4.0/PKG-INFO +0 -159
  48. isospecpy-2.4.0/README.md +0 -137
  49. isospecpy-2.4.0/VERSION +0 -1
  50. isospecpy-2.4.0/src/IsoSpec++/fasta.h +0 -70
  51. isospecpy-2.4.0/src/IsoSpec++/isoMath.h +0 -107
  52. isospecpy-2.4.0/src/IsoSpec++/operators.h +0 -150
  53. isospecpy-2.4.0/src/IsoSpec++/python-build.cpp +0 -23
  54. {isospecpy-2.4.0 → isospecpy-2.6.0}/LICENCE +0 -0
  55. {isospecpy-2.4.0 → isospecpy-2.6.0}/skbuild/CMakeLists.txt +0 -0
  56. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/IsaDispatch.cmake +0 -0
  57. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/allocator.cpp +0 -0
  58. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/conf.h +0 -0
  59. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/dirtyAllocator.cpp +0 -0
  60. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isa_kernels_avx.cpp +0 -0
  61. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isa_kernels_baseline.cpp +0 -0
  62. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isa_kernels_v3.cpp +0 -0
  63. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/isa_kernels_v4.cpp +0 -0
  64. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/iso_simd.h +0 -0
  65. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/mman.cpp +0 -0
  66. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/mman.h +0 -0
  67. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/platform_incl.h +0 -0
  68. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpec++/summator.h +0 -0
  69. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/Advanced.py +0 -0
  70. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/Distributions.py +0 -0
  71. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/Formulas.py +0 -0
  72. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/IsoSpecPyOld.py +0 -0
  73. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/__init__.py +0 -0
  74. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/__main__.py +0 -0
  75. {isospecpy-2.4.0 → isospecpy-2.6.0}/src/IsoSpecPy/confs_passthrough.py +0 -0
@@ -0,0 +1,269 @@
1
+ Metadata-Version: 2.4
2
+ Name: IsoSpecPy
3
+ Version: 2.6.0
4
+ Summary: IsoSpecPy is a Python library for computing isotopic distributions of molecules.
5
+ Keywords: isotopic distribution,mass spectrometry,chemistry,isotopic envelope,isotopologues
6
+ Author: Michał Startek, Mateusz Łącki
7
+ License-Expression: BSD-2-Clause
8
+ License-File: LICENCE
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Programming Language :: Python :: 3.15
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Development Status :: 5 - Production/Stable
21
+ Classifier: Operating System :: OS Independent
22
+ Project-URL: Homepage, https://github.com/MatteoLacki/IsoSpec
23
+ Project-URL: Repository, https://github.com/MatteoLacki/IsoSpec
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: cffi
26
+ Provides-Extra: testing
27
+ Requires-Dist: pytest; extra == "testing"
28
+ Requires-Dist: OldIsoSpecPy; extra == "testing"
29
+ Description-Content-Type: text/markdown
30
+
31
+ # IsoSpec
32
+
33
+ IsoSpec is a fine-structure isotopic distribution calculator for chemical formulas — given a molecule, it computes the masses and probabilities of its isotopologues. It is fast enough to handle large molecules (proteins, oligonucleotides) where the full distribution has astronomically many configurations, and it returns provably-optimal subsets that cover a chosen fraction of the total probability mass.
34
+
35
+ IsoSpec is primarily used as a library by mass spectrometry software. It is implemented in C++ (`src/IsoSpec++/`) and shipped with first-class bindings for Python (`IsoSpecPy`, on PyPI), R (`IsoSpecR`, on CRAN) and Rust (`isospec`, the crate in `rust/`).
36
+
37
+ ## Installation
38
+
39
+ ### Python
40
+
41
+ ```bash
42
+ pip install IsoSpecPy
43
+ ```
44
+
45
+ The wheel bundles the C++ library — no separate native install required. Compatible with CPython and PyPy on Linux, macOS, Windows, and Cygwin/MinGW. Python ≥ 3.9.
46
+
47
+ To build from source: `pip install .` from a checkout. A C++20 compiler is required.
48
+
49
+ ### R
50
+
51
+ ```r
52
+ install.packages("IsoSpecR")
53
+ ```
54
+
55
+ From source: `cd src && R CMD build IsoSpecR && R CMD INSTALL IsoSpecR_*.tar.gz`.
56
+
57
+ ### Rust
58
+
59
+ ```toml
60
+ [dependencies]
61
+ isospec = "0.1"
62
+ ```
63
+
64
+ The build script compiles the bundled C++ library; a C++20 compiler is all that is needed. See `rust/README.md`.
65
+
66
+ ### C++ library
67
+
68
+ ```bash
69
+ cd src/IsoSpec++
70
+ make # produces libIsoSpec++.so
71
+ ```
72
+
73
+ Or via CMake from the repo root:
74
+
75
+ ```bash
76
+ cmake -B build -DCMAKE_BUILD_TYPE=Release
77
+ cmake --build build
78
+ sudo cmake --install build
79
+ ```
80
+
81
+ Requires a C++20 compiler. CMake produces both shared and static libraries and installs headers under `${CMAKE_INSTALL_INCLUDEDIR}/IsoSpec++`.
82
+
83
+ ## Quick start
84
+
85
+ The most common usage is `IsoTotalProb`: ask for the smallest set of isotopologues whose summed probability covers a chosen fraction of the spectrum (typically 0.99–0.9999). The result is a materialized envelope — arrays of masses and probabilities you can read directly.
86
+
87
+ ### Python
88
+
89
+ ```python
90
+ import IsoSpecPy
91
+
92
+ # Isotopologues covering at least 99.9% of the probability mass of water.
93
+ iso = IsoSpecPy.IsoTotalProb(formula="H2O1", prob_to_cover=0.999)
94
+
95
+ for mass, prob in zip(iso.masses, iso.probs):
96
+ print(mass, prob)
97
+
98
+ # From an amino-acid sequence:
99
+ iso = IsoSpecPy.IsoTotalProb(peptide_sequence="AAAPPGQAAC", prob_to_cover=0.999)
100
+ print(list(zip(iso.masses, iso.probs)))
101
+
102
+ # ...optionally carrying [UNIMOD:<id>] modification tags (the notation this
103
+ # monorepo's SAGE search-engine fork writes: [UNIMOD:<id>]-SEQUENCE prefix
104
+ # for an N-terminal mod, X[UNIMOD:<id>] inline for an internal one,
105
+ # SEQUENCE-[UNIMOD:<id>] suffix for a C-terminal one):
106
+ iso = IsoSpecPy.IsoTotalProb(peptide_sequence="PEPTC[UNIMOD:4]DEK", prob_to_cover=0.999)
107
+ print(list(zip(iso.masses, iso.probs)))
108
+
109
+ # Mind the water: peptide_sequence= yields the *residue* composition, without
110
+ # the terminal H2O of a free peptide, so the masses above are 18.0106 Da
111
+ # lighter than the neutral molecule. Add it explicitly for the real peptide
112
+ # (the C++ Iso::FromFASTAWithMods below adds it by default -- the two APIs
113
+ # differ here, and this is the older Python behaviour kept for compatibility):
114
+ iso = IsoSpecPy.IsoTotalProb(peptide_sequence="PEPTC[UNIMOD:4]DEK", formula="H2O1",
115
+ prob_to_cover=0.999)
116
+
117
+ # Or get just the composition (element -> atom count), no envelope:
118
+ composition = IsoSpecPy.ParsePeptideSequence("PEPTC[UNIMOD:4]DEK")
119
+ print(dict(composition)) # {'H': 60, 'C': 39, 'N': 10, 'O': 16, 'S': 1}
120
+ ```
121
+
122
+ Resolves against a table of ~980 Unimod entries embedded in the library (isotope-labelled and glycan/derivatization "brick" modifications are excluded -- they aren't representable as a pure elemental delta). Pass `unimod_db_path="/path/to/table.csv"` to either function to resolve against a different table instead (same `id,name,mono_mass,composition` CSV shape as the embedded default).
123
+
124
+ See `Examples/Python/` for radiolabelling, custom elements, binned spectra, and FASTA/Unimod modifications.
125
+
126
+ ### C++
127
+
128
+ ```cpp
129
+ #include "IsoSpec++/isoSpec++.h"
130
+ #include "IsoSpec++/fixedEnvelopes.h"
131
+
132
+ using namespace IsoSpec;
133
+
134
+ int main() {
135
+ FixedEnvelope iso = FixedEnvelope::FromTotalProb("H2O1", 0.999, true, true);
136
+
137
+ for (size_t i = 0; i < iso.confs_no(); ++i) {
138
+ std::cout << iso.mass(i) << '\t' << iso.prob(i) << '\n';
139
+ }
140
+ }
141
+ ```
142
+
143
+ From a peptide sequence, optionally carrying `[UNIMOD:<id>]` modification tags (`fasta_mods.h`; same notation as the Python/R examples above):
144
+
145
+ ```cpp
146
+ #include "IsoSpec++/isoSpec++.h"
147
+ #include "IsoSpec++/fasta_mods.h"
148
+ #include "IsoSpec++/fixedEnvelopes.h"
149
+
150
+ using namespace IsoSpec;
151
+
152
+ int main() {
153
+ FixedEnvelope iso = FixedEnvelope::FromTotalProb(
154
+ Iso::FromFASTAWithMods("PEPTC[UNIMOD:4]DEK"), 0.999, true, true);
155
+
156
+ for (size_t i = 0; i < iso.confs_no(); ++i) {
157
+ std::cout << iso.mass(i) << '\t' << iso.prob(i) << '\n';
158
+ }
159
+ }
160
+ ```
161
+
162
+ `Iso::FromFASTAWithMods` takes an optional trailing `unimod_db_path` argument to resolve against a different Unimod table instead of the one embedded at compile time. For just the composition, no envelope, call `parse_fasta_with_mods`/`parse_fasta_with_mods_full` directly (or the allocation-free `_into` forms, for a hot loop over many sequences) — see `fasta_mods.h`.
163
+
164
+ Quickest way to build:
165
+
166
+ ```bash
167
+ clang++ -std=c++20 water.cpp src/IsoSpec++/unity-build.cpp -o water
168
+ ```
169
+
170
+ (`unity-build.cpp` is a single translation unit that `#include`s every source file — see `Examples/C++/COMPILING`.)
171
+
172
+ ### Rust
173
+
174
+ ```rust
175
+ use isospec::{Iso, PeptideOptions};
176
+
177
+ fn main() -> Result<(), isospec::Error> {
178
+ let iso = Iso::from_formula("H2O1")?;
179
+ for (mass, prob) in iso.envelope().total_prob(0.999)?.iter() {
180
+ println!("{mass}\t{prob}");
181
+ }
182
+
183
+ // A modified peptide; unlike Python's peptide_sequence=, the default
184
+ // here includes the terminal water.
185
+ let peptide = Iso::from_peptide("PEPTC[UNIMOD:4]DEK", &PeptideOptions::default())?;
186
+ println!("{}", peptide.monoisotopic_mass());
187
+ Ok(())
188
+ }
189
+ ```
190
+
191
+ See `rust/examples/` for binned spectra, generators with configurations, and labelled elements.
192
+
193
+ ### R
194
+
195
+ ```r
196
+ library(IsoSpecR)
197
+ water <- c(H = 2, O = 1)
198
+ IsoSpecify(molecule = water, stopCondition = 0.999)
199
+ ```
200
+
201
+ `IsoSpecify` only ever takes a raw named-integer-vector `molecule` -- it has no sequence-string parameter -- so a `[UNIMOD:<id>]`-annotated peptide sequence takes two calls instead of one: `RParsePeptideSequence()` to resolve the sequence into a composition, then `IsoSpecify()` as above.
202
+
203
+ ```r
204
+ composition <- RParsePeptideSequence("PEPTC[UNIMOD:4]DEK")
205
+ print(composition) # H C N O S
206
+ # 60 39 10 16 1
207
+ IsoSpecify(molecule = composition, stopCondition = 0.999)
208
+ ```
209
+
210
+ Like the Python `ParsePeptideSequence`, this returns the residue composition without a free peptide's terminal H2O -- add `c(H = 2, O = 1)` to it for the neutral molecule. A modification whose delta removes more atoms of an element than the bare sequence provides gives a negative count (`"G[UNIMOD:11]"` nets `H -1`, `S -1`); `IsoSpecify` rejects such a vector rather than quietly dropping those elements.
211
+
212
+ The rest of the API takes the same named vector, and treats an envelope as what `IsoSpecify` returns, a matrix with columns `mass` and `prob`:
213
+
214
+ ```r
215
+ insulin <- c(C = 254, H = 377, N = 65, O = 75, S = 6)
216
+ IsoProperties(insulin)$averageMass # also mono/lightest/heaviest/mode peaks, variance
217
+ IsoBinned(insulin, binWidth = 1) # unit-resolution distribution, computed directly
218
+ IsoStochastic(insulin, ionicCurrent = 1e4, seed = 1) # simulated, reproducible measurement
219
+ wassersteinDistance(IsoSpecify(insulin, 0.999), IsoSpecify(c(C = 254, H = 377, N = 65, O = 74, S = 7), 0.999))
220
+ ```
221
+
222
+ with `envelopeConvolve`, `envelopeLinearCombination`, `envelopeBin` and `envelopeResample` for envelope arithmetic, and `orientedWassersteinDistance`, `abyssalWassersteinDistance` and `wassersteinMatch` beside `wassersteinDistance`.
223
+
224
+ See `Examples/R/` for radiolabelling, full-spectrum extraction, Unimod-annotated sequences, and binned, simulated and compared envelopes.
225
+
226
+ ## Advanced features
227
+
228
+ - **Alternative algorithms** — for cases where `IsoTotalProb` isn't the right shape:
229
+ - `IsoThreshold` — all isotopologues with probability above a fixed threshold.
230
+ - `IsoStochastic` — simulate a measured spectrum by sampling integer ion counts.
231
+ - `IsoBinned` — histogram-style envelope at a chosen bin width.
232
+ - **FASTA/peptide-sequence support** — build an `Iso` directly from an amino-acid sequence, with `[UNIMOD:<id>]` modification tags recognized inline; optionally include the N/C-terminal water.
233
+ - **Custom isotopic tables** — override natural abundances per element (e.g. for radio- or stable-isotope labelling). See `Examples/*/radiolabelling.*`.
234
+ - **Fixed-envelope arithmetic** — addition, normalization, convolution, binning, resampling, Wasserstein distances.
235
+ - **Reproducible sampling** — stochastic envelopes and resampling take a seed (`seed=` in Python and R, `seeded_rng()` in C++, `with_seed` in Rust); a seed gives the same counts on every platform and in every binding.
236
+ - **Nominal-mass mode** — compute distributions over nucleon counts instead of real masses.
237
+ - **Thread safety** — the library never parallelises its own work, but independent molecules and envelopes may be used from any number of threads, and a shared molecule or envelope may be read from several at once.
238
+ - **Streaming generators (for performance)** — the tabulated functions above materialize the full envelope into arrays. For large molecules where that uses too much memory, or for pipelines that consume one configuration at a time (e.g. binning on the fly), use the generator classes instead: `IsoThresholdGenerator`, `IsoLayeredGenerator`, `IsoOrderedGenerator`, `IsoStochasticGenerator`. They yield one isotopologue per call to `advanceToNextConfiguration()` without ever storing the full set. `IsoOrderedGenerator` additionally streams in strict order of decreasing probability. Generators are in C++, Python and Rust; R has the materialized functions only.
239
+
240
+ Algorithmic details are in the papers cited below; the Supporting Information of each is the better starting point for implementation specifics.
241
+
242
+ ## Repository layout
243
+
244
+ ```
245
+ src/IsoSpec++/ C++ core library (the algorithms live here)
246
+ src/IsoSpecPy/ Python binding, loaded via cffi
247
+ src/IsoSpecR/ R binding, via Rcpp
248
+ rust/ Rust crate (Cargo.toml at the repository root), via cxx
249
+ Examples/ Working examples in each language
250
+ tests/ C, C++, and Python test suites (R's are in src/IsoSpecR/tests, Rust's in rust/tests)
251
+ skbuild/ scikit-build-core entry point used when installing the Python wheel
252
+ ```
253
+
254
+ ## Changes
255
+
256
+ `CHANGELOG` lists the major changes in each release (the Rust crate keeps its own, `rust/CHANGELOG.md`).
257
+
258
+ ## Citation
259
+
260
+ If IsoSpec is useful in your research, please cite:
261
+
262
+ - Łącki, M. K.; Valkenborg, D.; Startek, M. P. *IsoSpec2: Ultrafast Fine Structure Calculator.* Analytical Chemistry **2020**, *92* (14), 9472–9475. <https://doi.org/10.1021/acs.analchem.0c00959>
263
+ - Łącki, M. K.; Startek, M.; Valkenborg, D.; Gambin, A. *IsoSpec: Hyperfast Fine Structure Calculator.* Analytical Chemistry **2017**, *89* (6), 3272–3277. <https://doi.org/10.1021/acs.analchem.6b01459>
264
+
265
+ The Supporting Information of each paper has the algorithmic detail.
266
+
267
+ ## License
268
+
269
+ 2-clause BSD (see `LICENCE`). If you need different licensing terms, contact the authors. The authors appreciate (but do not require) being told when IsoSpec is used in other software.
@@ -0,0 +1,239 @@
1
+ # IsoSpec
2
+
3
+ IsoSpec is a fine-structure isotopic distribution calculator for chemical formulas — given a molecule, it computes the masses and probabilities of its isotopologues. It is fast enough to handle large molecules (proteins, oligonucleotides) where the full distribution has astronomically many configurations, and it returns provably-optimal subsets that cover a chosen fraction of the total probability mass.
4
+
5
+ IsoSpec is primarily used as a library by mass spectrometry software. It is implemented in C++ (`src/IsoSpec++/`) and shipped with first-class bindings for Python (`IsoSpecPy`, on PyPI), R (`IsoSpecR`, on CRAN) and Rust (`isospec`, the crate in `rust/`).
6
+
7
+ ## Installation
8
+
9
+ ### Python
10
+
11
+ ```bash
12
+ pip install IsoSpecPy
13
+ ```
14
+
15
+ The wheel bundles the C++ library — no separate native install required. Compatible with CPython and PyPy on Linux, macOS, Windows, and Cygwin/MinGW. Python ≥ 3.9.
16
+
17
+ To build from source: `pip install .` from a checkout. A C++20 compiler is required.
18
+
19
+ ### R
20
+
21
+ ```r
22
+ install.packages("IsoSpecR")
23
+ ```
24
+
25
+ From source: `cd src && R CMD build IsoSpecR && R CMD INSTALL IsoSpecR_*.tar.gz`.
26
+
27
+ ### Rust
28
+
29
+ ```toml
30
+ [dependencies]
31
+ isospec = "0.1"
32
+ ```
33
+
34
+ The build script compiles the bundled C++ library; a C++20 compiler is all that is needed. See `rust/README.md`.
35
+
36
+ ### C++ library
37
+
38
+ ```bash
39
+ cd src/IsoSpec++
40
+ make # produces libIsoSpec++.so
41
+ ```
42
+
43
+ Or via CMake from the repo root:
44
+
45
+ ```bash
46
+ cmake -B build -DCMAKE_BUILD_TYPE=Release
47
+ cmake --build build
48
+ sudo cmake --install build
49
+ ```
50
+
51
+ Requires a C++20 compiler. CMake produces both shared and static libraries and installs headers under `${CMAKE_INSTALL_INCLUDEDIR}/IsoSpec++`.
52
+
53
+ ## Quick start
54
+
55
+ The most common usage is `IsoTotalProb`: ask for the smallest set of isotopologues whose summed probability covers a chosen fraction of the spectrum (typically 0.99–0.9999). The result is a materialized envelope — arrays of masses and probabilities you can read directly.
56
+
57
+ ### Python
58
+
59
+ ```python
60
+ import IsoSpecPy
61
+
62
+ # Isotopologues covering at least 99.9% of the probability mass of water.
63
+ iso = IsoSpecPy.IsoTotalProb(formula="H2O1", prob_to_cover=0.999)
64
+
65
+ for mass, prob in zip(iso.masses, iso.probs):
66
+ print(mass, prob)
67
+
68
+ # From an amino-acid sequence:
69
+ iso = IsoSpecPy.IsoTotalProb(peptide_sequence="AAAPPGQAAC", prob_to_cover=0.999)
70
+ print(list(zip(iso.masses, iso.probs)))
71
+
72
+ # ...optionally carrying [UNIMOD:<id>] modification tags (the notation this
73
+ # monorepo's SAGE search-engine fork writes: [UNIMOD:<id>]-SEQUENCE prefix
74
+ # for an N-terminal mod, X[UNIMOD:<id>] inline for an internal one,
75
+ # SEQUENCE-[UNIMOD:<id>] suffix for a C-terminal one):
76
+ iso = IsoSpecPy.IsoTotalProb(peptide_sequence="PEPTC[UNIMOD:4]DEK", prob_to_cover=0.999)
77
+ print(list(zip(iso.masses, iso.probs)))
78
+
79
+ # Mind the water: peptide_sequence= yields the *residue* composition, without
80
+ # the terminal H2O of a free peptide, so the masses above are 18.0106 Da
81
+ # lighter than the neutral molecule. Add it explicitly for the real peptide
82
+ # (the C++ Iso::FromFASTAWithMods below adds it by default -- the two APIs
83
+ # differ here, and this is the older Python behaviour kept for compatibility):
84
+ iso = IsoSpecPy.IsoTotalProb(peptide_sequence="PEPTC[UNIMOD:4]DEK", formula="H2O1",
85
+ prob_to_cover=0.999)
86
+
87
+ # Or get just the composition (element -> atom count), no envelope:
88
+ composition = IsoSpecPy.ParsePeptideSequence("PEPTC[UNIMOD:4]DEK")
89
+ print(dict(composition)) # {'H': 60, 'C': 39, 'N': 10, 'O': 16, 'S': 1}
90
+ ```
91
+
92
+ Resolves against a table of ~980 Unimod entries embedded in the library (isotope-labelled and glycan/derivatization "brick" modifications are excluded -- they aren't representable as a pure elemental delta). Pass `unimod_db_path="/path/to/table.csv"` to either function to resolve against a different table instead (same `id,name,mono_mass,composition` CSV shape as the embedded default).
93
+
94
+ See `Examples/Python/` for radiolabelling, custom elements, binned spectra, and FASTA/Unimod modifications.
95
+
96
+ ### C++
97
+
98
+ ```cpp
99
+ #include "IsoSpec++/isoSpec++.h"
100
+ #include "IsoSpec++/fixedEnvelopes.h"
101
+
102
+ using namespace IsoSpec;
103
+
104
+ int main() {
105
+ FixedEnvelope iso = FixedEnvelope::FromTotalProb("H2O1", 0.999, true, true);
106
+
107
+ for (size_t i = 0; i < iso.confs_no(); ++i) {
108
+ std::cout << iso.mass(i) << '\t' << iso.prob(i) << '\n';
109
+ }
110
+ }
111
+ ```
112
+
113
+ From a peptide sequence, optionally carrying `[UNIMOD:<id>]` modification tags (`fasta_mods.h`; same notation as the Python/R examples above):
114
+
115
+ ```cpp
116
+ #include "IsoSpec++/isoSpec++.h"
117
+ #include "IsoSpec++/fasta_mods.h"
118
+ #include "IsoSpec++/fixedEnvelopes.h"
119
+
120
+ using namespace IsoSpec;
121
+
122
+ int main() {
123
+ FixedEnvelope iso = FixedEnvelope::FromTotalProb(
124
+ Iso::FromFASTAWithMods("PEPTC[UNIMOD:4]DEK"), 0.999, true, true);
125
+
126
+ for (size_t i = 0; i < iso.confs_no(); ++i) {
127
+ std::cout << iso.mass(i) << '\t' << iso.prob(i) << '\n';
128
+ }
129
+ }
130
+ ```
131
+
132
+ `Iso::FromFASTAWithMods` takes an optional trailing `unimod_db_path` argument to resolve against a different Unimod table instead of the one embedded at compile time. For just the composition, no envelope, call `parse_fasta_with_mods`/`parse_fasta_with_mods_full` directly (or the allocation-free `_into` forms, for a hot loop over many sequences) — see `fasta_mods.h`.
133
+
134
+ Quickest way to build:
135
+
136
+ ```bash
137
+ clang++ -std=c++20 water.cpp src/IsoSpec++/unity-build.cpp -o water
138
+ ```
139
+
140
+ (`unity-build.cpp` is a single translation unit that `#include`s every source file — see `Examples/C++/COMPILING`.)
141
+
142
+ ### Rust
143
+
144
+ ```rust
145
+ use isospec::{Iso, PeptideOptions};
146
+
147
+ fn main() -> Result<(), isospec::Error> {
148
+ let iso = Iso::from_formula("H2O1")?;
149
+ for (mass, prob) in iso.envelope().total_prob(0.999)?.iter() {
150
+ println!("{mass}\t{prob}");
151
+ }
152
+
153
+ // A modified peptide; unlike Python's peptide_sequence=, the default
154
+ // here includes the terminal water.
155
+ let peptide = Iso::from_peptide("PEPTC[UNIMOD:4]DEK", &PeptideOptions::default())?;
156
+ println!("{}", peptide.monoisotopic_mass());
157
+ Ok(())
158
+ }
159
+ ```
160
+
161
+ See `rust/examples/` for binned spectra, generators with configurations, and labelled elements.
162
+
163
+ ### R
164
+
165
+ ```r
166
+ library(IsoSpecR)
167
+ water <- c(H = 2, O = 1)
168
+ IsoSpecify(molecule = water, stopCondition = 0.999)
169
+ ```
170
+
171
+ `IsoSpecify` only ever takes a raw named-integer-vector `molecule` -- it has no sequence-string parameter -- so a `[UNIMOD:<id>]`-annotated peptide sequence takes two calls instead of one: `RParsePeptideSequence()` to resolve the sequence into a composition, then `IsoSpecify()` as above.
172
+
173
+ ```r
174
+ composition <- RParsePeptideSequence("PEPTC[UNIMOD:4]DEK")
175
+ print(composition) # H C N O S
176
+ # 60 39 10 16 1
177
+ IsoSpecify(molecule = composition, stopCondition = 0.999)
178
+ ```
179
+
180
+ Like the Python `ParsePeptideSequence`, this returns the residue composition without a free peptide's terminal H2O -- add `c(H = 2, O = 1)` to it for the neutral molecule. A modification whose delta removes more atoms of an element than the bare sequence provides gives a negative count (`"G[UNIMOD:11]"` nets `H -1`, `S -1`); `IsoSpecify` rejects such a vector rather than quietly dropping those elements.
181
+
182
+ The rest of the API takes the same named vector, and treats an envelope as what `IsoSpecify` returns, a matrix with columns `mass` and `prob`:
183
+
184
+ ```r
185
+ insulin <- c(C = 254, H = 377, N = 65, O = 75, S = 6)
186
+ IsoProperties(insulin)$averageMass # also mono/lightest/heaviest/mode peaks, variance
187
+ IsoBinned(insulin, binWidth = 1) # unit-resolution distribution, computed directly
188
+ IsoStochastic(insulin, ionicCurrent = 1e4, seed = 1) # simulated, reproducible measurement
189
+ wassersteinDistance(IsoSpecify(insulin, 0.999), IsoSpecify(c(C = 254, H = 377, N = 65, O = 74, S = 7), 0.999))
190
+ ```
191
+
192
+ with `envelopeConvolve`, `envelopeLinearCombination`, `envelopeBin` and `envelopeResample` for envelope arithmetic, and `orientedWassersteinDistance`, `abyssalWassersteinDistance` and `wassersteinMatch` beside `wassersteinDistance`.
193
+
194
+ See `Examples/R/` for radiolabelling, full-spectrum extraction, Unimod-annotated sequences, and binned, simulated and compared envelopes.
195
+
196
+ ## Advanced features
197
+
198
+ - **Alternative algorithms** — for cases where `IsoTotalProb` isn't the right shape:
199
+ - `IsoThreshold` — all isotopologues with probability above a fixed threshold.
200
+ - `IsoStochastic` — simulate a measured spectrum by sampling integer ion counts.
201
+ - `IsoBinned` — histogram-style envelope at a chosen bin width.
202
+ - **FASTA/peptide-sequence support** — build an `Iso` directly from an amino-acid sequence, with `[UNIMOD:<id>]` modification tags recognized inline; optionally include the N/C-terminal water.
203
+ - **Custom isotopic tables** — override natural abundances per element (e.g. for radio- or stable-isotope labelling). See `Examples/*/radiolabelling.*`.
204
+ - **Fixed-envelope arithmetic** — addition, normalization, convolution, binning, resampling, Wasserstein distances.
205
+ - **Reproducible sampling** — stochastic envelopes and resampling take a seed (`seed=` in Python and R, `seeded_rng()` in C++, `with_seed` in Rust); a seed gives the same counts on every platform and in every binding.
206
+ - **Nominal-mass mode** — compute distributions over nucleon counts instead of real masses.
207
+ - **Thread safety** — the library never parallelises its own work, but independent molecules and envelopes may be used from any number of threads, and a shared molecule or envelope may be read from several at once.
208
+ - **Streaming generators (for performance)** — the tabulated functions above materialize the full envelope into arrays. For large molecules where that uses too much memory, or for pipelines that consume one configuration at a time (e.g. binning on the fly), use the generator classes instead: `IsoThresholdGenerator`, `IsoLayeredGenerator`, `IsoOrderedGenerator`, `IsoStochasticGenerator`. They yield one isotopologue per call to `advanceToNextConfiguration()` without ever storing the full set. `IsoOrderedGenerator` additionally streams in strict order of decreasing probability. Generators are in C++, Python and Rust; R has the materialized functions only.
209
+
210
+ Algorithmic details are in the papers cited below; the Supporting Information of each is the better starting point for implementation specifics.
211
+
212
+ ## Repository layout
213
+
214
+ ```
215
+ src/IsoSpec++/ C++ core library (the algorithms live here)
216
+ src/IsoSpecPy/ Python binding, loaded via cffi
217
+ src/IsoSpecR/ R binding, via Rcpp
218
+ rust/ Rust crate (Cargo.toml at the repository root), via cxx
219
+ Examples/ Working examples in each language
220
+ tests/ C, C++, and Python test suites (R's are in src/IsoSpecR/tests, Rust's in rust/tests)
221
+ skbuild/ scikit-build-core entry point used when installing the Python wheel
222
+ ```
223
+
224
+ ## Changes
225
+
226
+ `CHANGELOG` lists the major changes in each release (the Rust crate keeps its own, `rust/CHANGELOG.md`).
227
+
228
+ ## Citation
229
+
230
+ If IsoSpec is useful in your research, please cite:
231
+
232
+ - Łącki, M. K.; Valkenborg, D.; Startek, M. P. *IsoSpec2: Ultrafast Fine Structure Calculator.* Analytical Chemistry **2020**, *92* (14), 9472–9475. <https://doi.org/10.1021/acs.analchem.0c00959>
233
+ - Łącki, M. K.; Startek, M.; Valkenborg, D.; Gambin, A. *IsoSpec: Hyperfast Fine Structure Calculator.* Analytical Chemistry **2017**, *89* (6), 3272–3277. <https://doi.org/10.1021/acs.analchem.6b01459>
234
+
235
+ The Supporting Information of each paper has the algorithmic detail.
236
+
237
+ ## License
238
+
239
+ 2-clause BSD (see `LICENCE`). If you need different licensing terms, contact the authors. The authors appreciate (but do not require) being told when IsoSpec is used in other software.
@@ -0,0 +1 @@
1
+ 2.6.0
@@ -13,6 +13,15 @@ regex = "^\\s*(?P<value>[0-9]+(?:\\.[0-9]+)*)"
13
13
 
14
14
  [tool.scikit-build]
15
15
  cmake.source-dir = "skbuild"
16
+ # The compiled part is not a Python extension module: it is a plain C-ABI
17
+ # shared library that isoFFI.py dlopen()s through cffi's ABI mode, and it never
18
+ # includes Python.h or links libpython. So the wheel is tagged py3-none-<plat>
19
+ # rather than per-interpreter (cp3XX) or Stable ABI (abi3): one binary per
20
+ # platform serves every CPython -- free-threaded builds included, which abi3
21
+ # does not cover -- and PyPy too. cibuildwheel builds it once per platform and
22
+ # only *tests* it under each remaining interpreter. Should the library ever
23
+ # start using the CPython API, this must go back to per-interpreter tags.
24
+ wheel.py-api = "py3"
16
25
 
17
26
  [project]
18
27
  name = "IsoSpecPy"
@@ -23,10 +32,18 @@ license = "BSD-2-Clause"
23
32
  license-files = ["LICENCE"]
24
33
  authors = [{name = "Michał Startek"}, {name = "Mateusz Łącki"}]
25
34
  readme = "README.md"
26
- requires-python = ">=3.6"
35
+ requires-python = ">=3.9"
27
36
  classifiers = [
28
37
  "Programming Language :: Python :: 3",
29
- "Programming Language :: Python :: 3.6",
38
+ "Programming Language :: Python :: 3.9",
39
+ "Programming Language :: Python :: 3.10",
40
+ "Programming Language :: Python :: 3.11",
41
+ "Programming Language :: Python :: 3.12",
42
+ "Programming Language :: Python :: 3.13",
43
+ "Programming Language :: Python :: 3.14",
44
+ "Programming Language :: Python :: 3.15",
45
+ "Programming Language :: Python :: Implementation :: CPython",
46
+ "Programming Language :: Python :: Implementation :: PyPy",
30
47
  "Topic :: Software Development :: Libraries :: Python Modules",
31
48
  "Development Status :: 5 - Production/Stable",
32
49
  "Operating System :: OS Independent",