IsoSpecPy 2.5.0__tar.gz → 2.6.1__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.
- {isospecpy-2.5.0 → isospecpy-2.6.1}/PKG-INFO +63 -9
- {isospecpy-2.5.0 → isospecpy-2.6.1}/README.md +61 -6
- isospecpy-2.6.1/VERSION +1 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/pyproject.toml +1 -2
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/aligned_ptr.h +59 -9
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/allocator.h +8 -1
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/composition.h +9 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/cwrapper.cpp +165 -65
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/cwrapper.h +49 -2
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/dirtyAllocator.h +2 -1
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/fasta.cpp +28 -1
- isospecpy-2.6.1/src/IsoSpec++/fasta.h +120 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/fasta_mods.cpp +62 -30
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/fasta_mods.h +7 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/fixedEnvelopes.cpp +610 -223
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/fixedEnvelopes.h +38 -8
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_dispatch.cpp +90 -32
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernel_impl.h +17 -6
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels.h +11 -2
- isospecpy-2.6.1/src/IsoSpec++/isoMath.cpp +342 -0
- isospecpy-2.6.1/src/IsoSpec++/isoMath.h +183 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isoSpec++.cpp +192 -117
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isoSpec++.h +221 -34
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/marginalTrek++.cpp +152 -49
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/marginalTrek++.h +31 -11
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/misc.cpp +2 -7
- isospecpy-2.6.1/src/IsoSpec++/operators.h +64 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/platform.h +14 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/pod_vector.h +88 -23
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/unimod.cpp +24 -16
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/unimod.h +4 -5
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/unity-build.cpp +0 -1
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/IsoSpecPy.py +137 -51
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/isoFFI.py +36 -1
- isospecpy-2.5.0/VERSION +0 -1
- isospecpy-2.5.0/src/IsoSpec++/fasta.h +0 -70
- isospecpy-2.5.0/src/IsoSpec++/isoMath.cpp +0 -181
- isospecpy-2.5.0/src/IsoSpec++/isoMath.h +0 -121
- isospecpy-2.5.0/src/IsoSpec++/operators.cpp +0 -38
- isospecpy-2.5.0/src/IsoSpec++/operators.h +0 -150
- {isospecpy-2.5.0 → isospecpy-2.6.1}/LICENCE +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/skbuild/CMakeLists.txt +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/IsaDispatch.cmake +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/allocator.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/btrd.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/composition.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/conf.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/dirtyAllocator.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/element_lookup.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/element_lookup.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/element_tables.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/element_tables.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels_avx.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels_baseline.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels_v3.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels_v4.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/iso_simd.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/misc.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/mman.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/mman.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/platform_incl.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/python-build.cpp +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/summator.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/unimod_support.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpec++/unimod_table_data.h +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/Advanced.py +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/Distributions.py +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/Formulas.py +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/IsoSpecPyOld.py +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/PeriodicTbl.py +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/__init__.py +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/__main__.py +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/approximations.py +0 -0
- {isospecpy-2.5.0 → isospecpy-2.6.1}/src/IsoSpecPy/confs_passthrough.py +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: IsoSpecPy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.1
|
|
4
4
|
Summary: IsoSpecPy is a Python library for computing isotopic distributions of molecules.
|
|
5
5
|
Keywords: isotopic distribution,mass spectrometry,chemistry,isotopic envelope,isotopologues
|
|
6
6
|
Author: Michał Startek, Mateusz Łącki
|
|
7
7
|
License-Expression: BSD-2-Clause
|
|
8
8
|
License-File: LICENCE
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -21,7 +20,7 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
21
20
|
Classifier: Operating System :: OS Independent
|
|
22
21
|
Project-URL: Homepage, https://github.com/MatteoLacki/IsoSpec
|
|
23
22
|
Project-URL: Repository, https://github.com/MatteoLacki/IsoSpec
|
|
24
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.10
|
|
25
24
|
Requires-Dist: cffi
|
|
26
25
|
Provides-Extra: testing
|
|
27
26
|
Requires-Dist: pytest; extra == "testing"
|
|
@@ -32,7 +31,7 @@ Description-Content-Type: text/markdown
|
|
|
32
31
|
|
|
33
32
|
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
33
|
|
|
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)
|
|
34
|
+
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
35
|
|
|
37
36
|
## Installation
|
|
38
37
|
|
|
@@ -42,7 +41,7 @@ IsoSpec is primarily used as a library by mass spectrometry software. It is impl
|
|
|
42
41
|
pip install IsoSpecPy
|
|
43
42
|
```
|
|
44
43
|
|
|
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.
|
|
44
|
+
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.10.
|
|
46
45
|
|
|
47
46
|
To build from source: `pip install .` from a checkout. A C++20 compiler is required.
|
|
48
47
|
|
|
@@ -54,6 +53,21 @@ install.packages("IsoSpecR")
|
|
|
54
53
|
|
|
55
54
|
From source: `cd src && R CMD build IsoSpecR && R CMD INSTALL IsoSpecR_*.tar.gz`.
|
|
56
55
|
|
|
56
|
+
### Rust
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
cargo add isospec
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
or, in `Cargo.toml`:
|
|
63
|
+
|
|
64
|
+
```toml
|
|
65
|
+
[dependencies]
|
|
66
|
+
isospec = "0.2"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The crate ([crates.io](https://crates.io/crates/isospec), API documentation on [docs.rs](https://docs.rs/isospec)) carries the C++ sources and compiles them in its build script; a C++20 compiler is all that is needed. See `rust/README.md`.
|
|
70
|
+
|
|
57
71
|
### C++ library
|
|
58
72
|
|
|
59
73
|
```bash
|
|
@@ -160,6 +174,27 @@ clang++ -std=c++20 water.cpp src/IsoSpec++/unity-build.cpp -o water
|
|
|
160
174
|
|
|
161
175
|
(`unity-build.cpp` is a single translation unit that `#include`s every source file — see `Examples/C++/COMPILING`.)
|
|
162
176
|
|
|
177
|
+
### Rust
|
|
178
|
+
|
|
179
|
+
```rust
|
|
180
|
+
use isospec::{Iso, PeptideOptions};
|
|
181
|
+
|
|
182
|
+
fn main() -> Result<(), isospec::Error> {
|
|
183
|
+
let iso = Iso::from_formula("H2O1")?;
|
|
184
|
+
for (mass, prob) in iso.envelope().total_prob(0.999)?.iter() {
|
|
185
|
+
println!("{mass}\t{prob}");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// A modified peptide; unlike Python's peptide_sequence=, the default
|
|
189
|
+
// here includes the terminal water.
|
|
190
|
+
let peptide = Iso::from_peptide("PEPTC[UNIMOD:4]DEK", &PeptideOptions::default())?;
|
|
191
|
+
println!("{}", peptide.monoisotopic_mass());
|
|
192
|
+
Ok(())
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
See `rust/examples/` for binned spectra, generators with configurations, and labelled elements.
|
|
197
|
+
|
|
163
198
|
### R
|
|
164
199
|
|
|
165
200
|
```r
|
|
@@ -179,7 +214,19 @@ IsoSpecify(molecule = composition, stopCondition = 0.999)
|
|
|
179
214
|
|
|
180
215
|
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
216
|
|
|
182
|
-
|
|
217
|
+
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`:
|
|
218
|
+
|
|
219
|
+
```r
|
|
220
|
+
insulin <- c(C = 254, H = 377, N = 65, O = 75, S = 6)
|
|
221
|
+
IsoProperties(insulin)$averageMass # also mono/lightest/heaviest/mode peaks, variance
|
|
222
|
+
IsoBinned(insulin, binWidth = 1) # unit-resolution distribution, computed directly
|
|
223
|
+
IsoStochastic(insulin, ionicCurrent = 1e4, seed = 1) # simulated, reproducible measurement
|
|
224
|
+
wassersteinDistance(IsoSpecify(insulin, 0.999), IsoSpecify(c(C = 254, H = 377, N = 65, O = 74, S = 7), 0.999))
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
with `envelopeConvolve`, `envelopeLinearCombination`, `envelopeBin` and `envelopeResample` for envelope arithmetic, and `orientedWassersteinDistance`, `abyssalWassersteinDistance` and `wassersteinMatch` beside `wassersteinDistance`.
|
|
228
|
+
|
|
229
|
+
See `Examples/R/` for radiolabelling, full-spectrum extraction, Unimod-annotated sequences, and binned, simulated and compared envelopes.
|
|
183
230
|
|
|
184
231
|
## Advanced features
|
|
185
232
|
|
|
@@ -189,9 +236,11 @@ See `Examples/R/` for radiolabelling, full-spectrum extraction, and Unimod-annot
|
|
|
189
236
|
- `IsoBinned` — histogram-style envelope at a chosen bin width.
|
|
190
237
|
- **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.
|
|
191
238
|
- **Custom isotopic tables** — override natural abundances per element (e.g. for radio- or stable-isotope labelling). See `Examples/*/radiolabelling.*`.
|
|
192
|
-
- **Fixed-envelope arithmetic** — addition, normalization, convolution, Wasserstein
|
|
239
|
+
- **Fixed-envelope arithmetic** — addition, normalization, convolution, binning, resampling, Wasserstein distances.
|
|
240
|
+
- **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.
|
|
193
241
|
- **Nominal-mass mode** — compute distributions over nucleon counts instead of real masses.
|
|
194
|
-
- **
|
|
242
|
+
- **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.
|
|
243
|
+
- **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.
|
|
195
244
|
|
|
196
245
|
Algorithmic details are in the papers cited below; the Supporting Information of each is the better starting point for implementation specifics.
|
|
197
246
|
|
|
@@ -201,11 +250,16 @@ Algorithmic details are in the papers cited below; the Supporting Information of
|
|
|
201
250
|
src/IsoSpec++/ C++ core library (the algorithms live here)
|
|
202
251
|
src/IsoSpecPy/ Python binding, loaded via cffi
|
|
203
252
|
src/IsoSpecR/ R binding, via Rcpp
|
|
253
|
+
rust/ Rust crate (Cargo.toml at the repository root), via cxx
|
|
204
254
|
Examples/ Working examples in each language
|
|
205
|
-
tests/ C, C++, and Python test suites
|
|
255
|
+
tests/ C, C++, and Python test suites (R's are in src/IsoSpecR/tests, Rust's in rust/tests)
|
|
206
256
|
skbuild/ scikit-build-core entry point used when installing the Python wheel
|
|
207
257
|
```
|
|
208
258
|
|
|
259
|
+
## Changes
|
|
260
|
+
|
|
261
|
+
`CHANGELOG` lists the major changes in each release (the Rust crate keeps its own, `rust/CHANGELOG.md`).
|
|
262
|
+
|
|
209
263
|
## Citation
|
|
210
264
|
|
|
211
265
|
If IsoSpec is useful in your research, please cite:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
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
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)
|
|
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
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ IsoSpec is primarily used as a library by mass spectrometry software. It is impl
|
|
|
12
12
|
pip install IsoSpecPy
|
|
13
13
|
```
|
|
14
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.
|
|
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.10.
|
|
16
16
|
|
|
17
17
|
To build from source: `pip install .` from a checkout. A C++20 compiler is required.
|
|
18
18
|
|
|
@@ -24,6 +24,21 @@ install.packages("IsoSpecR")
|
|
|
24
24
|
|
|
25
25
|
From source: `cd src && R CMD build IsoSpecR && R CMD INSTALL IsoSpecR_*.tar.gz`.
|
|
26
26
|
|
|
27
|
+
### Rust
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cargo add isospec
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
or, in `Cargo.toml`:
|
|
34
|
+
|
|
35
|
+
```toml
|
|
36
|
+
[dependencies]
|
|
37
|
+
isospec = "0.2"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The crate ([crates.io](https://crates.io/crates/isospec), API documentation on [docs.rs](https://docs.rs/isospec)) carries the C++ sources and compiles them in its build script; a C++20 compiler is all that is needed. See `rust/README.md`.
|
|
41
|
+
|
|
27
42
|
### C++ library
|
|
28
43
|
|
|
29
44
|
```bash
|
|
@@ -130,6 +145,27 @@ clang++ -std=c++20 water.cpp src/IsoSpec++/unity-build.cpp -o water
|
|
|
130
145
|
|
|
131
146
|
(`unity-build.cpp` is a single translation unit that `#include`s every source file — see `Examples/C++/COMPILING`.)
|
|
132
147
|
|
|
148
|
+
### Rust
|
|
149
|
+
|
|
150
|
+
```rust
|
|
151
|
+
use isospec::{Iso, PeptideOptions};
|
|
152
|
+
|
|
153
|
+
fn main() -> Result<(), isospec::Error> {
|
|
154
|
+
let iso = Iso::from_formula("H2O1")?;
|
|
155
|
+
for (mass, prob) in iso.envelope().total_prob(0.999)?.iter() {
|
|
156
|
+
println!("{mass}\t{prob}");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// A modified peptide; unlike Python's peptide_sequence=, the default
|
|
160
|
+
// here includes the terminal water.
|
|
161
|
+
let peptide = Iso::from_peptide("PEPTC[UNIMOD:4]DEK", &PeptideOptions::default())?;
|
|
162
|
+
println!("{}", peptide.monoisotopic_mass());
|
|
163
|
+
Ok(())
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
See `rust/examples/` for binned spectra, generators with configurations, and labelled elements.
|
|
168
|
+
|
|
133
169
|
### R
|
|
134
170
|
|
|
135
171
|
```r
|
|
@@ -149,7 +185,19 @@ IsoSpecify(molecule = composition, stopCondition = 0.999)
|
|
|
149
185
|
|
|
150
186
|
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.
|
|
151
187
|
|
|
152
|
-
|
|
188
|
+
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`:
|
|
189
|
+
|
|
190
|
+
```r
|
|
191
|
+
insulin <- c(C = 254, H = 377, N = 65, O = 75, S = 6)
|
|
192
|
+
IsoProperties(insulin)$averageMass # also mono/lightest/heaviest/mode peaks, variance
|
|
193
|
+
IsoBinned(insulin, binWidth = 1) # unit-resolution distribution, computed directly
|
|
194
|
+
IsoStochastic(insulin, ionicCurrent = 1e4, seed = 1) # simulated, reproducible measurement
|
|
195
|
+
wassersteinDistance(IsoSpecify(insulin, 0.999), IsoSpecify(c(C = 254, H = 377, N = 65, O = 74, S = 7), 0.999))
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
with `envelopeConvolve`, `envelopeLinearCombination`, `envelopeBin` and `envelopeResample` for envelope arithmetic, and `orientedWassersteinDistance`, `abyssalWassersteinDistance` and `wassersteinMatch` beside `wassersteinDistance`.
|
|
199
|
+
|
|
200
|
+
See `Examples/R/` for radiolabelling, full-spectrum extraction, Unimod-annotated sequences, and binned, simulated and compared envelopes.
|
|
153
201
|
|
|
154
202
|
## Advanced features
|
|
155
203
|
|
|
@@ -159,9 +207,11 @@ See `Examples/R/` for radiolabelling, full-spectrum extraction, and Unimod-annot
|
|
|
159
207
|
- `IsoBinned` — histogram-style envelope at a chosen bin width.
|
|
160
208
|
- **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.
|
|
161
209
|
- **Custom isotopic tables** — override natural abundances per element (e.g. for radio- or stable-isotope labelling). See `Examples/*/radiolabelling.*`.
|
|
162
|
-
- **Fixed-envelope arithmetic** — addition, normalization, convolution, Wasserstein
|
|
210
|
+
- **Fixed-envelope arithmetic** — addition, normalization, convolution, binning, resampling, Wasserstein distances.
|
|
211
|
+
- **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.
|
|
163
212
|
- **Nominal-mass mode** — compute distributions over nucleon counts instead of real masses.
|
|
164
|
-
- **
|
|
213
|
+
- **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.
|
|
214
|
+
- **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.
|
|
165
215
|
|
|
166
216
|
Algorithmic details are in the papers cited below; the Supporting Information of each is the better starting point for implementation specifics.
|
|
167
217
|
|
|
@@ -171,11 +221,16 @@ Algorithmic details are in the papers cited below; the Supporting Information of
|
|
|
171
221
|
src/IsoSpec++/ C++ core library (the algorithms live here)
|
|
172
222
|
src/IsoSpecPy/ Python binding, loaded via cffi
|
|
173
223
|
src/IsoSpecR/ R binding, via Rcpp
|
|
224
|
+
rust/ Rust crate (Cargo.toml at the repository root), via cxx
|
|
174
225
|
Examples/ Working examples in each language
|
|
175
|
-
tests/ C, C++, and Python test suites
|
|
226
|
+
tests/ C, C++, and Python test suites (R's are in src/IsoSpecR/tests, Rust's in rust/tests)
|
|
176
227
|
skbuild/ scikit-build-core entry point used when installing the Python wheel
|
|
177
228
|
```
|
|
178
229
|
|
|
230
|
+
## Changes
|
|
231
|
+
|
|
232
|
+
`CHANGELOG` lists the major changes in each release (the Rust crate keeps its own, `rust/CHANGELOG.md`).
|
|
233
|
+
|
|
179
234
|
## Citation
|
|
180
235
|
|
|
181
236
|
If IsoSpec is useful in your research, please cite:
|
isospecpy-2.6.1/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.6.1
|
|
@@ -32,10 +32,9 @@ license = "BSD-2-Clause"
|
|
|
32
32
|
license-files = ["LICENCE"]
|
|
33
33
|
authors = [{name = "Michał Startek"}, {name = "Mateusz Łącki"}]
|
|
34
34
|
readme = "README.md"
|
|
35
|
-
requires-python = ">=3.
|
|
35
|
+
requires-python = ">=3.10"
|
|
36
36
|
classifiers = [
|
|
37
37
|
"Programming Language :: Python :: 3",
|
|
38
|
-
"Programming Language :: Python :: 3.9",
|
|
39
38
|
"Programming Language :: Python :: 3.10",
|
|
40
39
|
"Programming Language :: Python :: 3.11",
|
|
41
40
|
"Programming Language :: Python :: 3.12",
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
// elsewhere for plain huge-page mmap): that layer has no remap concept at
|
|
37
37
|
// all on Windows and no equivalent of mremap on Darwin, so each platform's
|
|
38
38
|
// real growth primitive is used directly instead:
|
|
39
|
-
// - Linux
|
|
39
|
+
// - Linux (any libc with mremap): mmap + mremap(MREMAP_MAYMOVE) + munmap.
|
|
40
|
+
// - Other UNIX (FreeBSD, ...: no mremap): growth maps a fresh region and
|
|
41
|
+
// copies, shrink unmaps the tail in place.
|
|
40
42
|
// - Apple: mmap/munmap for create/destroy (Darwin has no mremap); growth
|
|
41
43
|
// re-wires pages into a fresh mapping via mach_vm_remap instead of
|
|
42
44
|
// copying, shrink just munmaps the tail in place.
|
|
@@ -57,25 +59,41 @@
|
|
|
57
59
|
|
|
58
60
|
// The small-allocation backend needs an aligned allocator that can also be
|
|
59
61
|
// deallocated with a plain std::free() (release() promises exactly that).
|
|
60
|
-
// glibc/libc++'s std::aligned_alloc satisfies both at once.
|
|
62
|
+
// glibc/libc++'s std::aligned_alloc satisfies both at once. Windows' CRT
|
|
63
|
+
// (MSVC's, and the msvcrt/UCRT that MinGW, i.e. R's Rtools, links against) has
|
|
61
64
|
// no C11 aligned_alloc at all, only _aligned_malloc/_aligned_free -- and
|
|
62
65
|
// memory from those two is NOT free()-compatible (the CRT stores its own
|
|
63
66
|
// offset bookkeeping ahead of the returned pointer, which plain free() does
|
|
64
|
-
// not know how to unwind). So on
|
|
67
|
+
// not know how to unwind). So on Windows the small backend uses
|
|
65
68
|
// _aligned_malloc/_aligned_free for its own storage (still genuinely
|
|
66
69
|
// Alignment-aligned, as marginalTrek++.h's use of this class relies on),
|
|
67
70
|
// and release() falls back to materialising a fresh plain-malloc()'d copy
|
|
68
71
|
// to keep its free()-compatibility promise -- the same thing it already
|
|
69
72
|
// does to escape a VM-backed region.
|
|
70
|
-
#if defined(
|
|
73
|
+
#if defined(_WIN32)
|
|
71
74
|
#define ISOSPEC_ALIGNED_PTR_SMALL_BACKEND_FREE_COMPATIBLE 0
|
|
72
75
|
#else
|
|
73
76
|
#define ISOSPEC_ALIGNED_PTR_SMALL_BACKEND_FREE_COMPATIBLE 1
|
|
74
77
|
#endif
|
|
75
78
|
|
|
76
79
|
#if ISOSPEC_WE_ARE_ON_WINDOWS
|
|
80
|
+
// Without NOMINMAX, <windows.h> defines min and max as macros, which break
|
|
81
|
+
// every later std::min(...) and numeric_limits<T>::max() in the including
|
|
82
|
+
// translation unit. Defined only around this include, so that the macro
|
|
83
|
+
// itself does not leak into the caller's code (though a later <windows.h>
|
|
84
|
+
// there is then a no-op, without the macros); a caller who included
|
|
85
|
+
// <windows.h> first, macros and all, is covered by the parenthesised
|
|
86
|
+
// (std::min)(...) calls in the public headers.
|
|
87
|
+
#ifndef NOMINMAX
|
|
88
|
+
#define NOMINMAX
|
|
89
|
+
#define ISOSPEC_UNDEF_NOMINMAX
|
|
90
|
+
#endif
|
|
77
91
|
#include <windows.h>
|
|
78
|
-
#
|
|
92
|
+
#ifdef ISOSPEC_UNDEF_NOMINMAX
|
|
93
|
+
#undef NOMINMAX
|
|
94
|
+
#undef ISOSPEC_UNDEF_NOMINMAX
|
|
95
|
+
#endif
|
|
96
|
+
#if defined(_WIN32)
|
|
79
97
|
#include <malloc.h> // _aligned_malloc / _aligned_free
|
|
80
98
|
#endif
|
|
81
99
|
#elif ISOSPEC_WE_ARE_ON_UNIX_YAY
|
|
@@ -303,7 +321,26 @@ inline void vm_resize(vm_region& r, std::size_t new_bytes)
|
|
|
303
321
|
r.size = new_size;
|
|
304
322
|
}
|
|
305
323
|
|
|
306
|
-
#else //
|
|
324
|
+
#else // Other UNIX: Linux with mremap(), the rest (FreeBSD, ...) without
|
|
325
|
+
|
|
326
|
+
// ThreadSanitizer intercepts mmap() and munmap() but not mremap(), so a region
|
|
327
|
+
// that mremap() moves away from is freed behind its back and keeps its old
|
|
328
|
+
// shadow state. When another thread's mremap() later lands on those
|
|
329
|
+
// addresses, TSan reports its writes as racing with the previous owner's --
|
|
330
|
+
// pure address reuse, reproduced with two threads and nothing else. Under
|
|
331
|
+
// TSan, growth therefore goes through mmap + copy + munmap, which it does see.
|
|
332
|
+
#if defined(__SANITIZE_THREAD__)
|
|
333
|
+
#define ISOSPEC_ALIGNED_PTR_UNDER_TSAN 1
|
|
334
|
+
#elif defined(__has_feature)
|
|
335
|
+
#if __has_feature(thread_sanitizer)
|
|
336
|
+
#define ISOSPEC_ALIGNED_PTR_UNDER_TSAN 1
|
|
337
|
+
#endif
|
|
338
|
+
#endif
|
|
339
|
+
|
|
340
|
+
// mremap() is Linux's alone; MREMAP_MAYMOVE, from <sys/mman.h>, is the test.
|
|
341
|
+
#if defined(MREMAP_MAYMOVE) && !defined(ISOSPEC_ALIGNED_PTR_UNDER_TSAN)
|
|
342
|
+
#define ISOSPEC_ALIGNED_PTR_USE_MREMAP 1
|
|
343
|
+
#endif
|
|
307
344
|
|
|
308
345
|
inline void vm_resize(vm_region& r, std::size_t new_bytes)
|
|
309
346
|
{
|
|
@@ -311,11 +348,24 @@ inline void vm_resize(vm_region& r, std::size_t new_bytes)
|
|
|
311
348
|
if (new_size == r.size)
|
|
312
349
|
return;
|
|
313
350
|
|
|
351
|
+
#if !defined(ISOSPEC_ALIGNED_PTR_USE_MREMAP)
|
|
352
|
+
if (new_size < r.size) {
|
|
353
|
+
// Shrink in place: base address is untouched, just drop the tail.
|
|
354
|
+
::munmap(static_cast<char*>(r.base) + new_size, r.size - new_size);
|
|
355
|
+
r.size = new_size;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
vm_region moved = vm_create(new_bytes);
|
|
359
|
+
std::memcpy(moved.base, r.base, (std::min)(r.size, moved.size));
|
|
360
|
+
vm_destroy(r);
|
|
361
|
+
r = moved;
|
|
362
|
+
#else
|
|
314
363
|
void* p = ::mremap(r.base, r.size, new_size, MREMAP_MAYMOVE);
|
|
315
364
|
if (p == MAP_FAILED)
|
|
316
365
|
throw std::bad_alloc();
|
|
317
366
|
r.base = p;
|
|
318
367
|
r.size = new_size;
|
|
368
|
+
#endif
|
|
319
369
|
}
|
|
320
370
|
|
|
321
371
|
#endif // __APPLE__
|
|
@@ -549,7 +599,7 @@ class aligned_unique_ptr {
|
|
|
549
599
|
// still has to copy -- there is nothing yet to remap.
|
|
550
600
|
aligned_ptr_detail::vm_region r = aligned_ptr_detail::vm_create(new_bytes);
|
|
551
601
|
if (ptr_) {
|
|
552
|
-
std::memcpy(r.base, ptr_, std::min(capacity_bytes_, new_bytes));
|
|
602
|
+
std::memcpy(r.base, ptr_, (std::min)(capacity_bytes_, new_bytes));
|
|
553
603
|
free_small(ptr_);
|
|
554
604
|
}
|
|
555
605
|
region_ = r;
|
|
@@ -562,7 +612,7 @@ class aligned_unique_ptr {
|
|
|
562
612
|
|
|
563
613
|
T* new_ptr = allocate_small(n);
|
|
564
614
|
if (ptr_) {
|
|
565
|
-
std::memcpy(new_ptr, ptr_, std::min(capacity_bytes_, new_bytes));
|
|
615
|
+
std::memcpy(new_ptr, ptr_, (std::min)(capacity_bytes_, new_bytes));
|
|
566
616
|
free_small(ptr_);
|
|
567
617
|
}
|
|
568
618
|
ptr_ = new_ptr;
|
|
@@ -573,7 +623,7 @@ class aligned_unique_ptr {
|
|
|
573
623
|
// bare std::free() / C free(), regardless of which backend produced it.
|
|
574
624
|
// Whenever the current backend isn't already free()-compatible on its
|
|
575
625
|
// own -- a VM-backed region always needs this, and so does the small
|
|
576
|
-
// backend on
|
|
626
|
+
// backend on Windows (see ISOSPEC_ALIGNED_PTR_SMALL_BACKEND_FREE_COMPATIBLE
|
|
577
627
|
// above) -- this materialises a fresh plain-malloc()'d copy instead.
|
|
578
628
|
// That copy is the whole cost of calling this in those cases;
|
|
579
629
|
// release_with_deleter() below avoids it when the caller can accept a
|
|
@@ -23,6 +23,13 @@
|
|
|
23
23
|
namespace IsoSpec
|
|
24
24
|
{
|
|
25
25
|
|
|
26
|
+
// Configurations per allocator slab. Small on purpose: with glibc, a slab of
|
|
27
|
+
// up to 1032 bytes (4 isotopes x 64 ints = 1024) is recycled through the
|
|
28
|
+
// per-thread tcache when the next molecule is built, so many small molecules
|
|
29
|
+
// in a row run 16-20% faster than with 1000 (2000 tryptic peptides, threshold
|
|
30
|
+
// generator, x86-64-v3 and v4). Single large calculations do not notice.
|
|
31
|
+
constexpr int default_tab_size = 64;
|
|
32
|
+
|
|
26
33
|
template <typename T> inline void copyConf(
|
|
27
34
|
const T* source, T* destination,
|
|
28
35
|
int dim
|
|
@@ -39,7 +46,7 @@ template <typename T> class Allocator
|
|
|
39
46
|
pod_vector<T*> prevTabs;
|
|
40
47
|
|
|
41
48
|
public:
|
|
42
|
-
explicit Allocator(const int dim, const int tabSize =
|
|
49
|
+
explicit Allocator(const int dim, const int tabSize = default_tab_size);
|
|
43
50
|
~Allocator();
|
|
44
51
|
|
|
45
52
|
Allocator(const Allocator& other) = delete;
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
#pragma once
|
|
29
29
|
|
|
30
|
+
#include <string_view>
|
|
30
31
|
#include <vector>
|
|
31
32
|
|
|
32
33
|
#include "isoSpec++.h"
|
|
@@ -87,6 +88,14 @@ struct ExpandedComposition {
|
|
|
87
88
|
}
|
|
88
89
|
};
|
|
89
90
|
|
|
91
|
+
//! A formula string read as a composition: each element once, in the order
|
|
92
|
+
//! written, counts signed ("H-2O-1" is accepted -- a composition may be a
|
|
93
|
+
//! delta; build_iso_from_composition rejects negatives when one becomes a
|
|
94
|
+
//! molecule). Throws std::invalid_argument for a malformed formula, an unknown
|
|
95
|
+
//! element or one written twice, std::out_of_range for a count past int. The
|
|
96
|
+
//! same tokenizer and checks as parse_formula, which goes through it.
|
|
97
|
+
ElementComposition parse_formula_composition(std::string_view formula);
|
|
98
|
+
|
|
90
99
|
//! Resolves each element of `composition` to its isotopes' masses and
|
|
91
100
|
//! probabilities from element_tables.h, filling a caller-owned `out`
|
|
92
101
|
//! (cleared first).
|