IsoSpecPy 2.6.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.6.0 → isospecpy-2.6.1}/PKG-INFO +11 -6
- {isospecpy-2.6.0 → isospecpy-2.6.1}/README.md +9 -3
- isospecpy-2.6.1/VERSION +1 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/pyproject.toml +1 -2
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/cwrapper.cpp +96 -61
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/cwrapper.h +28 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/fixedEnvelopes.cpp +49 -19
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/fixedEnvelopes.h +14 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isoMath.cpp +135 -33
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isoMath.h +5 -11
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/marginalTrek++.cpp +13 -4
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/marginalTrek++.h +6 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/pod_vector.h +88 -23
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/IsoSpecPy.py +11 -5
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/isoFFI.py +23 -0
- isospecpy-2.6.0/VERSION +0 -1
- {isospecpy-2.6.0 → isospecpy-2.6.1}/LICENCE +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/skbuild/CMakeLists.txt +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/IsaDispatch.cmake +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/aligned_ptr.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/allocator.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/allocator.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/btrd.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/composition.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/composition.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/conf.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/dirtyAllocator.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/dirtyAllocator.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/element_lookup.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/element_lookup.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/element_tables.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/element_tables.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/fasta.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/fasta.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/fasta_mods.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/fasta_mods.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_dispatch.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernel_impl.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels_avx.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels_baseline.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels_v3.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isa_kernels_v4.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isoSpec++.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/isoSpec++.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/iso_simd.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/misc.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/misc.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/mman.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/mman.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/operators.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/platform.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/platform_incl.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/python-build.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/summator.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/unimod.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/unimod.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/unimod_support.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/unimod_table_data.h +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpec++/unity-build.cpp +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/Advanced.py +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/Distributions.py +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/Formulas.py +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/IsoSpecPyOld.py +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/PeriodicTbl.py +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/__init__.py +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/__main__.py +0 -0
- {isospecpy-2.6.0 → isospecpy-2.6.1}/src/IsoSpecPy/approximations.py +0 -0
- {isospecpy-2.6.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.6.
|
|
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"
|
|
@@ -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
|
|
|
@@ -56,12 +55,18 @@ From source: `cd src && R CMD build IsoSpecR && R CMD INSTALL IsoSpecR_*.tar.gz`
|
|
|
56
55
|
|
|
57
56
|
### Rust
|
|
58
57
|
|
|
58
|
+
```bash
|
|
59
|
+
cargo add isospec
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
or, in `Cargo.toml`:
|
|
63
|
+
|
|
59
64
|
```toml
|
|
60
65
|
[dependencies]
|
|
61
|
-
isospec = "0.
|
|
66
|
+
isospec = "0.2"
|
|
62
67
|
```
|
|
63
68
|
|
|
64
|
-
The
|
|
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`.
|
|
65
70
|
|
|
66
71
|
### C++ library
|
|
67
72
|
|
|
@@ -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
|
|
|
@@ -26,12 +26,18 @@ From source: `cd src && R CMD build IsoSpecR && R CMD INSTALL IsoSpecR_*.tar.gz`
|
|
|
26
26
|
|
|
27
27
|
### Rust
|
|
28
28
|
|
|
29
|
+
```bash
|
|
30
|
+
cargo add isospec
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
or, in `Cargo.toml`:
|
|
34
|
+
|
|
29
35
|
```toml
|
|
30
36
|
[dependencies]
|
|
31
|
-
isospec = "0.
|
|
37
|
+
isospec = "0.2"
|
|
32
38
|
```
|
|
33
39
|
|
|
34
|
-
The
|
|
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`.
|
|
35
41
|
|
|
36
42
|
### C++ library
|
|
37
43
|
|
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",
|
|
@@ -341,87 +341,122 @@ ISOSPEC_C_FN_CODES(IsoStochasticGenerator)
|
|
|
341
341
|
|
|
342
342
|
// ______________________________________________________ FixedEnvelopes
|
|
343
343
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
344
|
+
// Each envelope constructor comes in two forms. The plain one leaves the
|
|
345
|
+
// caller's Iso alone, so it fully copies it (marginal tables and all) for the
|
|
346
|
+
// generator inside to own. The *Consuming one takes the Iso over: it moves it
|
|
347
|
+
// into the generator and deletes the emptied handle before returning, whether
|
|
348
|
+
// or not it succeeded. Skipping the copy saves 0.65 us of the 4.5 us a
|
|
349
|
+
// 30-residue peptide takes in Binned at 1 Da and target 0.99 (x86-64-v3).
|
|
350
|
+
// The helpers are templates and lambdas, so they sit outside extern "C".
|
|
351
|
+
} // extern "C"
|
|
352
|
+
|
|
353
|
+
namespace
|
|
354
|
+
{
|
|
355
|
+
template<typename F>
|
|
356
|
+
void* envelope_from_iso(void* iso, bool consume, F&& make) noexcept
|
|
348
357
|
{
|
|
358
|
+
// Owned before c_guard runs anything, so that the handle goes on every path.
|
|
359
|
+
const std::unique_ptr<Iso> owned(consume ? reinterpret_cast<Iso*>(iso) : nullptr);
|
|
349
360
|
return c_guard([&]() -> void*
|
|
350
361
|
{ // Use copy elision to allocate on heap with named constructor
|
|
351
|
-
|
|
352
|
-
FixedEnvelope::
|
|
353
|
-
|
|
354
|
-
absolute,
|
|
355
|
-
get_confs));
|
|
362
|
+
if(consume)
|
|
363
|
+
return new FixedEnvelope(make(std::move(*owned)));
|
|
364
|
+
return new FixedEnvelope(make(Iso(*reinterpret_cast<const Iso*>(iso), true)));
|
|
356
365
|
});
|
|
357
366
|
}
|
|
358
367
|
|
|
359
|
-
void*
|
|
360
|
-
double target_coverage,
|
|
361
|
-
bool optimize,
|
|
362
|
-
bool get_confs)
|
|
368
|
+
void* threshold_envelope(void* iso, bool consume, double threshold, bool absolute, bool get_confs) noexcept
|
|
363
369
|
{
|
|
364
|
-
return
|
|
365
|
-
|
|
366
|
-
return new FixedEnvelope(
|
|
367
|
-
FixedEnvelope::FromTotalProb(Iso(*reinterpret_cast<const Iso*>(iso), true),
|
|
368
|
-
target_coverage,
|
|
369
|
-
optimize,
|
|
370
|
-
get_confs));
|
|
371
|
-
});
|
|
370
|
+
return envelope_from_iso(iso, consume, [&](Iso&& i)
|
|
371
|
+
{ return FixedEnvelope::FromThreshold(std::move(i), threshold, absolute, get_confs); });
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
-
void*
|
|
375
|
-
size_t no_molecules,
|
|
376
|
-
double precision,
|
|
377
|
-
double beta_bias,
|
|
378
|
-
bool get_confs)
|
|
374
|
+
void* total_prob_envelope(void* iso, bool consume, double target_coverage, bool optimize, bool get_confs) noexcept
|
|
379
375
|
{
|
|
380
|
-
return
|
|
381
|
-
|
|
382
|
-
return new FixedEnvelope(
|
|
383
|
-
FixedEnvelope::FromStochastic(Iso(*reinterpret_cast<const Iso*>(iso), true),
|
|
384
|
-
no_molecules,
|
|
385
|
-
precision,
|
|
386
|
-
beta_bias,
|
|
387
|
-
get_confs));
|
|
388
|
-
});
|
|
376
|
+
return envelope_from_iso(iso, consume, [&](Iso&& i)
|
|
377
|
+
{ return FixedEnvelope::FromTotalProb(std::move(i), target_coverage, optimize, get_confs); });
|
|
389
378
|
}
|
|
390
379
|
|
|
391
|
-
void*
|
|
392
|
-
|
|
393
|
-
double precision,
|
|
394
|
-
double beta_bias,
|
|
395
|
-
bool get_confs,
|
|
396
|
-
uint64_t seed)
|
|
380
|
+
void* stochastic_envelope(void* iso, bool consume, size_t no_molecules, double precision, double beta_bias,
|
|
381
|
+
bool get_confs) noexcept
|
|
397
382
|
{
|
|
398
|
-
return
|
|
383
|
+
return envelope_from_iso(iso, consume, [&](Iso&& i)
|
|
384
|
+
{ return FixedEnvelope::FromStochastic(std::move(i), no_molecules, precision, beta_bias, get_confs); });
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
void* stochastic_envelope_seeded(void* iso, bool consume, size_t no_molecules, double precision,
|
|
388
|
+
double beta_bias, bool get_confs, uint64_t seed) noexcept
|
|
389
|
+
{
|
|
390
|
+
return envelope_from_iso(iso, consume, [&](Iso&& i)
|
|
399
391
|
{
|
|
400
392
|
std::mt19937 rng = seeded_rng(seed);
|
|
401
|
-
return
|
|
402
|
-
FixedEnvelope::FromStochastic(Iso(*reinterpret_cast<const Iso*>(iso), true),
|
|
403
|
-
no_molecules,
|
|
404
|
-
precision,
|
|
405
|
-
beta_bias,
|
|
406
|
-
get_confs,
|
|
407
|
-
rng));
|
|
393
|
+
return FixedEnvelope::FromStochastic(std::move(i), no_molecules, precision, beta_bias, get_confs, rng);
|
|
408
394
|
});
|
|
409
395
|
}
|
|
410
396
|
|
|
397
|
+
void* binned_envelope(void* iso, bool consume, double target_total_prob, double bin_width,
|
|
398
|
+
double bin_middle) noexcept
|
|
399
|
+
{
|
|
400
|
+
return envelope_from_iso(iso, consume, [&](Iso&& i)
|
|
401
|
+
{ return FixedEnvelope::Binned(std::move(i), target_total_prob, bin_width, bin_middle); });
|
|
402
|
+
}
|
|
403
|
+
} // namespace
|
|
411
404
|
|
|
412
|
-
|
|
413
|
-
double target_total_prob,
|
|
414
|
-
double bin_width,
|
|
415
|
-
double bin_middle)
|
|
405
|
+
extern "C"
|
|
416
406
|
{
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
407
|
+
|
|
408
|
+
void* setupThresholdFixedEnvelope(void* iso, double threshold, bool absolute, bool get_confs)
|
|
409
|
+
{
|
|
410
|
+
return threshold_envelope(iso, false, threshold, absolute, get_confs);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
void* setupThresholdFixedEnvelopeConsuming(void* iso, double threshold, bool absolute, bool get_confs)
|
|
414
|
+
{
|
|
415
|
+
return threshold_envelope(iso, true, threshold, absolute, get_confs);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
void* setupTotalProbFixedEnvelope(void* iso, double target_coverage, bool optimize, bool get_confs)
|
|
419
|
+
{
|
|
420
|
+
return total_prob_envelope(iso, false, target_coverage, optimize, get_confs);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
void* setupTotalProbFixedEnvelopeConsuming(void* iso, double target_coverage, bool optimize, bool get_confs)
|
|
424
|
+
{
|
|
425
|
+
return total_prob_envelope(iso, true, target_coverage, optimize, get_confs);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
void* setupStochasticFixedEnvelope(void* iso, size_t no_molecules, double precision, double beta_bias,
|
|
429
|
+
bool get_confs)
|
|
430
|
+
{
|
|
431
|
+
return stochastic_envelope(iso, false, no_molecules, precision, beta_bias, get_confs);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
void* setupStochasticFixedEnvelopeConsuming(void* iso, size_t no_molecules, double precision, double beta_bias,
|
|
435
|
+
bool get_confs)
|
|
436
|
+
{
|
|
437
|
+
return stochastic_envelope(iso, true, no_molecules, precision, beta_bias, get_confs);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
void* setupStochasticFixedEnvelopeSeeded(void* iso, size_t no_molecules, double precision, double beta_bias,
|
|
441
|
+
bool get_confs, uint64_t seed)
|
|
442
|
+
{
|
|
443
|
+
return stochastic_envelope_seeded(iso, false, no_molecules, precision, beta_bias, get_confs, seed);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
void* setupStochasticFixedEnvelopeSeededConsuming(void* iso, size_t no_molecules, double precision,
|
|
447
|
+
double beta_bias, bool get_confs, uint64_t seed)
|
|
448
|
+
{
|
|
449
|
+
return stochastic_envelope_seeded(iso, true, no_molecules, precision, beta_bias, get_confs, seed);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
void* setupBinnedFixedEnvelope(void* iso, double target_total_prob, double bin_width, double bin_middle)
|
|
453
|
+
{
|
|
454
|
+
return binned_envelope(iso, false, target_total_prob, bin_width, bin_middle);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
void* setupBinnedFixedEnvelopeConsuming(void* iso, double target_total_prob, double bin_width, double bin_middle)
|
|
458
|
+
{
|
|
459
|
+
return binned_envelope(iso, true, target_total_prob, bin_width, bin_middle);
|
|
425
460
|
}
|
|
426
461
|
|
|
427
462
|
void* setupFixedEnvelope(double* masses, double* probs, size_t size, bool mass_sorted, bool prob_sorted, double total_prob)
|
|
@@ -130,21 +130,39 @@ ISOSPEC_C_API void* setupIsoStochasticGeneratorSeeded(void* iso,
|
|
|
130
130
|
double beta_bias,
|
|
131
131
|
uint64_t seed);
|
|
132
132
|
|
|
133
|
+
/* Envelopes from an Iso. Each comes in two forms: the plain one leaves `iso`
|
|
134
|
+
untouched (it copies it, marginal tables and all), the *Consuming one takes
|
|
135
|
+
`iso` over and saves that copy (0.65 us of a 30-residue peptide's 4.5 us
|
|
136
|
+
binned at 1 Da). A *Consuming call deletes `iso` before returning, whether
|
|
137
|
+
or not it succeeds: never use or deleteIso() it afterwards. */
|
|
133
138
|
ISOSPEC_C_API void* setupThresholdFixedEnvelope(void* iso,
|
|
134
139
|
double threshold,
|
|
135
140
|
bool absolute,
|
|
136
141
|
bool get_confs);
|
|
142
|
+
ISOSPEC_C_API void* setupThresholdFixedEnvelopeConsuming(void* iso,
|
|
143
|
+
double threshold,
|
|
144
|
+
bool absolute,
|
|
145
|
+
bool get_confs);
|
|
137
146
|
|
|
138
147
|
ISOSPEC_C_API void* setupTotalProbFixedEnvelope(void* iso,
|
|
139
148
|
double taget_coverage,
|
|
140
149
|
bool optimize,
|
|
141
150
|
bool get_confs);
|
|
151
|
+
ISOSPEC_C_API void* setupTotalProbFixedEnvelopeConsuming(void* iso,
|
|
152
|
+
double taget_coverage,
|
|
153
|
+
bool optimize,
|
|
154
|
+
bool get_confs);
|
|
142
155
|
|
|
143
156
|
ISOSPEC_C_API void* setupStochasticFixedEnvelope(void* iso,
|
|
144
157
|
size_t no_molecules,
|
|
145
158
|
double precision,
|
|
146
159
|
double beta_bias,
|
|
147
160
|
bool get_confs);
|
|
161
|
+
ISOSPEC_C_API void* setupStochasticFixedEnvelopeConsuming(void* iso,
|
|
162
|
+
size_t no_molecules,
|
|
163
|
+
double precision,
|
|
164
|
+
double beta_bias,
|
|
165
|
+
bool get_confs);
|
|
148
166
|
|
|
149
167
|
ISOSPEC_C_API void* setupStochasticFixedEnvelopeSeeded(void* iso,
|
|
150
168
|
size_t no_molecules,
|
|
@@ -152,11 +170,21 @@ ISOSPEC_C_API void* setupStochasticFixedEnvelopeSeeded(void* iso,
|
|
|
152
170
|
double beta_bias,
|
|
153
171
|
bool get_confs,
|
|
154
172
|
uint64_t seed);
|
|
173
|
+
ISOSPEC_C_API void* setupStochasticFixedEnvelopeSeededConsuming(void* iso,
|
|
174
|
+
size_t no_molecules,
|
|
175
|
+
double precision,
|
|
176
|
+
double beta_bias,
|
|
177
|
+
bool get_confs,
|
|
178
|
+
uint64_t seed);
|
|
155
179
|
|
|
156
180
|
ISOSPEC_C_API void* setupBinnedFixedEnvelope(void* iso,
|
|
157
181
|
double target_total_prob,
|
|
158
182
|
double bin_width,
|
|
159
183
|
double bin_middle);
|
|
184
|
+
ISOSPEC_C_API void* setupBinnedFixedEnvelopeConsuming(void* iso,
|
|
185
|
+
double target_total_prob,
|
|
186
|
+
double bin_width,
|
|
187
|
+
double bin_middle);
|
|
160
188
|
|
|
161
189
|
ISOSPEC_C_API void freeReleasedArray(void* array);
|
|
162
190
|
|
|
@@ -1303,8 +1303,15 @@ template<bool tgetConfs> void FixedEnvelope::total_prob_init(Iso&& iso, double t
|
|
|
1303
1303
|
// recomputes land inside the smaller buffer rather than past its end.
|
|
1304
1304
|
this->_confs_no = end;
|
|
1305
1305
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1306
|
+
// Overhead in memory of 2x or more: shrink to fit, but only buffers big
|
|
1307
|
+
// enough for the waste to matter. Below the VM backend's threshold the
|
|
1308
|
+
// shrink is a fresh aligned_alloc plus a copy of every survivor, paid on
|
|
1309
|
+
// every call for at most a few hundred KiB of slack (C254H377N65O75S6 to
|
|
1310
|
+
// p = 0.9999: 25 KiB copied out of 64 KiB buffers, each call).
|
|
1311
|
+
size_t bytes_per_conf = 2 * sizeof(double);
|
|
1312
|
+
constexpr_if(tgetConfs)
|
|
1313
|
+
bytes_per_conf += this->allDimSizeofInt;
|
|
1314
|
+
if(end <= current_size/2 && current_size * bytes_per_conf >= aligned_ptr_detail::vm_backend_threshold())
|
|
1308
1315
|
this->template reallocate_memory<tgetConfs>(end);
|
|
1309
1316
|
}
|
|
1310
1317
|
|
|
@@ -1587,26 +1594,49 @@ FixedEnvelope FixedEnvelope::Binned(Iso&& iso, double target_total_prob, double
|
|
|
1587
1594
|
throw std::logic_error("Binned: the shifted bin grid starts below its first slot");
|
|
1588
1595
|
const size_t no_bins = static_cast<size_t>(last_slot) + 2;
|
|
1589
1596
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1597
|
+
// The accumulator spans the whole mass range, of which a large molecule
|
|
1598
|
+
// populates only a small part: mapped anonymously, the rest costs address
|
|
1599
|
+
// space only. A small one's range is mostly populated, and below
|
|
1600
|
+
// ISOSPEC_BINNED_MMAP_MIN_BYTES (see fixedEnvelopes.h) calloc's zeroing is
|
|
1601
|
+
// cheaper than mmap's syscalls and page faults. Above it calloc is no
|
|
1602
|
+
// substitute: glibc serves blocks up to its adaptive mmap threshold (as much
|
|
1603
|
+
// as 32 MiB) from the heap and memsets all of them.
|
|
1604
|
+
const size_t acc_bytes = sizeof(double)*no_bins;
|
|
1605
|
+
#if ISOSPEC_GOT_MMAN
|
|
1606
|
+
const bool mapped = acc_bytes >= ISOSPEC_BINNED_MMAP_MIN_BYTES;
|
|
1595
1607
|
#else
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1608
|
+
constexpr bool mapped = false;
|
|
1609
|
+
#endif
|
|
1610
|
+
|
|
1611
|
+
double* acc;
|
|
1612
|
+
if(mapped)
|
|
1613
|
+
{
|
|
1614
|
+
#if ISOSPEC_GOT_MMAN
|
|
1615
|
+
acc = reinterpret_cast<double*>(mmap(nullptr, acc_bytes, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
|
1616
|
+
if(acc == MAP_FAILED)
|
|
1617
|
+
throw std::bad_alloc();
|
|
1600
1618
|
#endif
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1619
|
+
}
|
|
1620
|
+
else
|
|
1621
|
+
{
|
|
1622
|
+
acc = reinterpret_cast<double*>(calloc(no_bins, sizeof(double)));
|
|
1623
|
+
if(acc == nullptr)
|
|
1624
|
+
throw std::bad_alloc();
|
|
1625
|
+
}
|
|
1626
|
+
// Keep the accumulator alive through generator construction and output
|
|
1627
|
+
// growth, both of which can throw.
|
|
1628
|
+
const auto release_acc = [acc_bytes, mapped](double* base) noexcept {
|
|
1605
1629
|
#if ISOSPEC_GOT_MMAN
|
|
1606
|
-
|
|
1630
|
+
if(mapped)
|
|
1631
|
+
{
|
|
1632
|
+
munmap(base, acc_bytes);
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1607
1635
|
#else
|
|
1608
|
-
|
|
1636
|
+
(void)acc_bytes;
|
|
1637
|
+
(void)mapped;
|
|
1609
1638
|
#endif
|
|
1639
|
+
free(base);
|
|
1610
1640
|
};
|
|
1611
1641
|
std::unique_ptr<double, decltype(release_acc)> acc_owner(acc, release_acc);
|
|
1612
1642
|
|
|
@@ -1622,8 +1652,8 @@ FixedEnvelope FixedEnvelope::Binned(Iso&& iso, double target_total_prob, double
|
|
|
1622
1652
|
# ifndef ISOSPEC_BINNED_HUGEPAGE_MIN_BYTES
|
|
1623
1653
|
# define ISOSPEC_BINNED_HUGEPAGE_MIN_BYTES (size_t(64) << 20) // 64 MiB
|
|
1624
1654
|
# endif
|
|
1625
|
-
if(
|
|
1626
|
-
madvise(acc,
|
|
1655
|
+
if(mapped && acc_bytes >= ISOSPEC_BINNED_HUGEPAGE_MIN_BYTES)
|
|
1656
|
+
madvise(acc, acc_bytes, MADV_HUGEPAGE);
|
|
1627
1657
|
#endif
|
|
1628
1658
|
|
|
1629
1659
|
std::ptrdiff_t nonzero_slot = 0;
|
|
@@ -43,6 +43,20 @@
|
|
|
43
43
|
# define ISOSPEC_BINNED_LAYER_MAXSTEP (-3.0)
|
|
44
44
|
#endif
|
|
45
45
|
|
|
46
|
+
// Binned() takes its bin accumulator from calloc below this size, and maps it
|
|
47
|
+
// anonymously at and above it. The mmap path costs a fixed 2-25 us (map, page
|
|
48
|
+
// faults, unmap: 2.5 on Alder Lake, 2 on M1 macOS, 8 under WSL2, 25 in a KVM
|
|
49
|
+
// guest), which calloc saves; calloc in turn zeroes the whole range, which the
|
|
50
|
+
// mmap path never touches beyond the populated bins. Measured as calloc/mmap
|
|
51
|
+
// time: glucose at 0.001 Da (241 KB) 0.43 / 0.21 / 0.63 / 0.43 on those four,
|
|
52
|
+
// a 1 kDa peptide at 0.01 Da (157 KB) 0.69 / 0.30 / 0.80 / 0.53. Where calloc
|
|
53
|
+
// starts to lose depends on how much of the range is populated: on Alder Lake,
|
|
54
|
+
// insulin breaks even at ~300 KB, a 1 kDa peptide at ~1 MB; the other hosts
|
|
55
|
+
// tolerate 1-2 MB. 256 KiB never loses on the primary target.
|
|
56
|
+
#ifndef ISOSPEC_BINNED_MMAP_MIN_BYTES
|
|
57
|
+
# define ISOSPEC_BINNED_MMAP_MIN_BYTES (std::size_t(256) << 10)
|
|
58
|
+
#endif
|
|
59
|
+
|
|
46
60
|
// The same knob for total_prob_init, which can afford a wider step than Binned:
|
|
47
61
|
// it trims afterwards, so overshooting the target costs only the work of
|
|
48
62
|
// generating what gets thrown away.
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
// NOLINT(legal/copyright)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
#include <algorithm>
|
|
13
14
|
#include <cmath>
|
|
15
|
+
#include <cstdint>
|
|
16
|
+
#include <limits>
|
|
14
17
|
#include <cstdlib>
|
|
15
18
|
#include <new>
|
|
16
19
|
#include "isoMath.h"
|
|
@@ -180,51 +183,150 @@ double NormalPDF(double x, double mean, double stdev)
|
|
|
180
183
|
|
|
181
184
|
const double sqrt_pi = 1.772453850905516027298167483341145182798;
|
|
182
185
|
|
|
183
|
-
|
|
186
|
+
namespace
|
|
184
187
|
{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
188
|
+
// The regularised incomplete gamma functions at a = k/2, k >= 1: their values
|
|
189
|
+
// P(a, s) and Q(a, s) = 1 - P(a, s), each computed directly so that neither
|
|
190
|
+
// loses its relative precision to cancellation in its tail, and the density
|
|
191
|
+
// dP/ds = s^(a-1) e^(-s) / Gamma(a), which every Halley step needs anyway.
|
|
192
|
+
// `lgamma_a` is lnGamma(a). Region by region, as Boost's gamma_p does it:
|
|
193
|
+
// s < a+1: P = s^a e^-s / Gamma(a+1) * sum_n s^n / ((a+1)...(a+n)),
|
|
194
|
+
// whose prefix is density * s / a and whose terms fall at once;
|
|
195
|
+
// s >= a+1: Q's finite sum, for k = 2m and k = 2m+1
|
|
196
|
+
// Q(m, s) = e^-s * sum_{j<m} s^j / j!
|
|
197
|
+
// Q(m+1/2, s) = erfc(sqrt(s)) + e^-s * sum_{j<m} s^(j+1/2) / Gamma(j+3/2),
|
|
198
|
+
// summed downward from its last term, which is the density:
|
|
199
|
+
// each term is the next one times (j+a-m)/s < 1, so nothing
|
|
200
|
+
// overflows whatever k, and the sum stops once the terms are
|
|
201
|
+
// below rounding.
|
|
202
|
+
struct GammaHalf
|
|
203
|
+
{
|
|
204
|
+
double P, Q, density;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
GammaHalf gamma_half(int k, double s, double lgamma_a)
|
|
208
|
+
{
|
|
209
|
+
const double a = 0.5 * k;
|
|
210
|
+
const double density = exp((a - 1.0) * log(s) - s - lgamma_a);
|
|
211
|
+
|
|
212
|
+
if(s < a + 1.0)
|
|
195
213
|
{
|
|
196
|
-
|
|
197
|
-
|
|
214
|
+
double t = 1.0, series = 1.0;
|
|
215
|
+
for(double d = a + 1.0; t > series * 0x1p-54; d += 1.0)
|
|
216
|
+
{
|
|
217
|
+
t *= s / d;
|
|
218
|
+
series += t;
|
|
219
|
+
}
|
|
220
|
+
const double P = density * (s / a) * series;
|
|
221
|
+
return {P, 1.0 - P, density};
|
|
198
222
|
}
|
|
199
223
|
|
|
200
|
-
|
|
201
|
-
for(;
|
|
224
|
+
double sum = 0.0, t = density;
|
|
225
|
+
for(int i = 0; i < k / 2 && t > sum * 0x1p-54; i++)
|
|
202
226
|
{
|
|
203
|
-
|
|
204
|
-
|
|
227
|
+
sum += t;
|
|
228
|
+
t *= (a - 1.0 - i) / s;
|
|
205
229
|
}
|
|
206
|
-
|
|
207
|
-
return
|
|
230
|
+
const double Q = (k % 2 == 0) ? sum : erfc(sqrt(s)) + sum;
|
|
231
|
+
return {1.0 - Q, Q, density};
|
|
208
232
|
}
|
|
233
|
+
} // namespace
|
|
209
234
|
|
|
210
|
-
double
|
|
235
|
+
double InverseChiSquareCDF2(int k, double x)
|
|
211
236
|
{
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
237
|
+
// q = 2s with P(k/2, s) = x. The initial guess is the larger of
|
|
238
|
+
// * Wilson and Hilferty (1931): (q/k)^(1/3) is close to normal with mean
|
|
239
|
+
// 1 - 2/(9k) and variance 2/(9k), so q ~ k (1 - 2/(9k) + z sqrt(2/(9k)))^3
|
|
240
|
+
// with z the normal quantile of x -- within a fraction of a percent
|
|
241
|
+
// away from the far tails;
|
|
242
|
+
// * the leading term of P's series, P(a, s) <= s^a / Gamma(a+1), whose
|
|
243
|
+
// inverse s = (x Gamma(a+1))^(1/a) is a lower bound on the root
|
|
244
|
+
// everywhere and accurate in the lower tail, where Wilson-Hilferty's
|
|
245
|
+
// cube can go negative.
|
|
246
|
+
// Then Halley's iteration (as in DiDonato and Morris 1986, and Boost's
|
|
247
|
+
// gamma_p_inv): f'' / f' = (a-1)/s - 1 for f = P(a, s) - x, so each step
|
|
248
|
+
// costs one evaluation of gamma_half, and from this guess two (rarely
|
|
249
|
+
// more; at most 10 in the far tails of k < 10) reach full precision. The callers need about 0.1% (the quantile
|
|
250
|
+
// only orders marginals by estimated size), but the steps past that cost
|
|
251
|
+
// little and make the function testable against reference values. No
|
|
252
|
+
// tgamma or pow: one exp and one log per step, lnGamma from lgamma_half.
|
|
253
|
+
//
|
|
254
|
+
// Defined everywhere, so that the sort keys built from it are never NaN:
|
|
255
|
+
// 0 for k <= 0 (no freedom) and for x <= 0 or NaN; x >= 1, whose quantile
|
|
256
|
+
// is infinite, is answered at the largest double below 1.
|
|
257
|
+
if(k <= 0 || !(x > 0.0))
|
|
258
|
+
return 0.0;
|
|
259
|
+
x = (std::min)(x, 1.0 - 0x1p-53);
|
|
260
|
+
if(k == 2)
|
|
261
|
+
return -2.0 * log1p(-x); // exponential with mean 2: exact
|
|
262
|
+
|
|
263
|
+
const double a = 0.5 * k;
|
|
264
|
+
// lnGamma(1/2) = ln(sqrt(pi)) is below lgamma_half's domain.
|
|
265
|
+
const double lgamma_a = k == 1 ? 0.5723649429247001 : lgamma_half(k);
|
|
266
|
+
|
|
267
|
+
double s_wh;
|
|
268
|
+
if(k == 1 && x >= 0.5)
|
|
269
|
+
{
|
|
270
|
+
// The square of a standard normal: q = z^2 for z the normal quantile
|
|
271
|
+
// at (1+x)/2, taken from its tail (1-x)/2, which keeps its precision;
|
|
272
|
+
// NormalCDFInverse's 4.5e-4 makes it a guess. Below the median the
|
|
273
|
+
// series bound is the better one.
|
|
274
|
+
const double z = NormalCDFInverse(0.5 * (1.0 - x));
|
|
275
|
+
s_wh = 0.5 * z * z;
|
|
276
|
+
}
|
|
277
|
+
else
|
|
278
|
+
{
|
|
279
|
+
const double v = 2.0 / (9.0 * k);
|
|
280
|
+
const double h = 1.0 - v + NormalCDFInverse(x) * sqrt(v);
|
|
281
|
+
s_wh = h > 0.0 ? a * h * h * h : 0.0;
|
|
282
|
+
}
|
|
283
|
+
const double lower_bound = exp((log(x) + lgamma_half(static_cast<std::int64_t>(k) + 2)) / a);
|
|
284
|
+
double s = (std::max)(s_wh, lower_bound);
|
|
285
|
+
if(!(s > 0.0))
|
|
286
|
+
return 0.0; // below the smallest double
|
|
287
|
+
|
|
288
|
+
// Solved for P below the median and for Q above it, in whichever tail
|
|
289
|
+
// keeps its relative precision; both are increasing in s.
|
|
290
|
+
const bool upper = x > 0.5;
|
|
291
|
+
const double target = upper ? 1.0 - x : x;
|
|
292
|
+
double lo = lower_bound, hi = std::numeric_limits<double>::infinity();
|
|
293
|
+
for(int iteration = 0; iteration < 100; iteration++)
|
|
294
|
+
{
|
|
295
|
+
const GammaHalf g = gamma_half(k, s, lgamma_a);
|
|
296
|
+
const double f = upper ? target - g.Q : g.P - target;
|
|
297
|
+
if(f == 0.0 || !(g.density > 0.0))
|
|
298
|
+
break;
|
|
299
|
+
if(f < 0.0)
|
|
300
|
+
lo = s;
|
|
221
301
|
else
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
302
|
+
hi = s;
|
|
303
|
+
const double newton = f / g.density;
|
|
304
|
+
const double curvature = 1.0 - 0.5 * newton * ((a - 1.0) / s - 1.0);
|
|
305
|
+
const double halley = curvature > 0.5 ? newton / curvature : newton;
|
|
306
|
+
// Converged once the step is below 2^-20 relative: Halley's error
|
|
307
|
+
// after a step of d is of order d^3, so taking that step puts s
|
|
308
|
+
// within rounding of the root (checked over k = 1..1000 and x from
|
|
309
|
+
// 1e-15 to 1 - 1e-15 against SciPy: worst 4e-14, one evaluation
|
|
310
|
+
// fewer than a 2^-40 test, the same answers). Checked before the
|
|
311
|
+
// bracket, which a step below one ulp of s cannot enter.
|
|
312
|
+
if(fabs(halley) <= s * 0x1p-20)
|
|
313
|
+
{
|
|
314
|
+
s -= halley;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
// A step leaving the bracket falls back to Newton's, then to
|
|
318
|
+
// bisection (or doubling, while the bracket has no upper end).
|
|
319
|
+
double next = s - halley;
|
|
320
|
+
if(!(next > lo && next < hi))
|
|
321
|
+
next = s - newton;
|
|
322
|
+
if(!(next > lo && next < hi))
|
|
323
|
+
next = std::isfinite(hi) ? 0.5 * (lo + hi) : 2.0 * (std::max)(s, lo);
|
|
324
|
+
s = next;
|
|
325
|
+
}
|
|
326
|
+
return 2.0 * s;
|
|
226
327
|
}
|
|
227
328
|
|
|
329
|
+
|
|
228
330
|
thread_local std::mt19937 random_gen(std::random_device{}());
|
|
229
331
|
|
|
230
332
|
size_t rdvariate_binom(size_t tries, double succ_prob, std::mt19937& rgen)
|
|
@@ -110,17 +110,11 @@ double NormalCDFInverse(double p, double mean, double stdev);
|
|
|
110
110
|
double NormalCDF(double x, double mean, double stdev);
|
|
111
111
|
double NormalPDF(double x, double mean = 0.0, double stdev = 1.0);
|
|
112
112
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
double
|
|
118
|
-
|
|
119
|
-
// Computes the inverse Cumulative Distribution Funcion of the Chi-Square distribution with k degrees of freedom
|
|
120
|
-
inline double InverseChiSquareCDF2(int k, double x)
|
|
121
|
-
{
|
|
122
|
-
return InverseLowerIncompleteGamma2(k, x*tgamma(static_cast<double>(k)/2.0)) * 2.0;
|
|
123
|
-
}
|
|
113
|
+
// The quantile function (inverse CDF) of the chi-square distribution with k
|
|
114
|
+
// degrees of freedom at x: q with P(k/2, q/2) = x, to about 1e-12 relative
|
|
115
|
+
// (tested against SciPy). 0 for k <= 0 and for x <= 0 or NaN; finite for
|
|
116
|
+
// x >= 1. Thread-safe: no lgamma.
|
|
117
|
+
double InverseChiSquareCDF2(int k, double x);
|
|
124
118
|
|
|
125
119
|
extern thread_local std::mt19937 random_gen;
|
|
126
120
|
|
|
@@ -736,8 +736,12 @@ bool LayeredMarginal::extend(double new_threshold, bool do_sort)
|
|
|
736
736
|
for(size_t ii = 0; ii < ISOSPEC_LPROB_GUARDIANS; ii++)
|
|
737
737
|
lProbs.pop_back(); // Remove the trailing -inf guardians; restored at the end
|
|
738
738
|
|
|
739
|
-
|
|
740
|
-
|
|
739
|
+
// Empty already (swapped with the drained fringe at the end of the last
|
|
740
|
+
// call), unless that call threw partway.
|
|
741
|
+
pod_vector<Conf>& new_fringe = next_fringe;
|
|
742
|
+
pod_vector<double>& new_fringe_unn_lprobs = next_fringe_unn_lprobs;
|
|
743
|
+
new_fringe.clear_keep_capacity();
|
|
744
|
+
new_fringe_unn_lprobs.clear_keep_capacity();
|
|
741
745
|
|
|
742
746
|
while(!fringe.empty())
|
|
743
747
|
{
|
|
@@ -809,10 +813,15 @@ bool LayeredMarginal::extend(double new_threshold, bool do_sort)
|
|
|
809
813
|
size_t to_sort_size = configurations.size() - probs.size();
|
|
810
814
|
if(to_sort_size > 0)
|
|
811
815
|
{
|
|
812
|
-
|
|
816
|
+
// get_inverse_order(), into the reused sort_order.
|
|
813
817
|
double* P = lProbs.data()+1+probs.size();
|
|
814
818
|
Conf* C = configurations.data()+probs.size();
|
|
815
|
-
|
|
819
|
+
sort_order.clear_keep_capacity();
|
|
820
|
+
sort_order.resize(to_sort_size);
|
|
821
|
+
size_t* O = sort_order.data();
|
|
822
|
+
for(size_t ii = 0; ii < to_sort_size; ii++)
|
|
823
|
+
O[ii] = ii;
|
|
824
|
+
std::sort(O, O + to_sort_size, [P](size_t i, size_t j) { return P[i] > P[j]; });
|
|
816
825
|
impose_order(O, to_sort_size, P, C);
|
|
817
826
|
}
|
|
818
827
|
}
|
|
@@ -402,6 +402,12 @@ class LayeredMarginal : public Marginal
|
|
|
402
402
|
pod_vector<Conf> configurations;
|
|
403
403
|
pod_vector<Conf> fringe;
|
|
404
404
|
pod_vector<double> fringe_unn_lprobs;
|
|
405
|
+
// extend()'s scratch, kept between calls so that a layer allocates
|
|
406
|
+
// nothing once they have grown: the fringe below the new threshold (then
|
|
407
|
+
// swapped with fringe), and the sort order of the new configurations.
|
|
408
|
+
pod_vector<Conf> next_fringe;
|
|
409
|
+
pod_vector<double> next_fringe_unn_lprobs;
|
|
410
|
+
pod_vector<size_t> sort_order;
|
|
405
411
|
Allocator<int> allocator;
|
|
406
412
|
pod_vector<double> lProbs;
|
|
407
413
|
pod_vector<double> probs;
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
#include <type_traits>
|
|
20
20
|
#include <cstdlib>
|
|
21
21
|
#include <cstddef>
|
|
22
|
+
#include <cstring>
|
|
22
23
|
#include <utility>
|
|
23
24
|
#include <new>
|
|
24
25
|
#include <algorithm>
|
|
@@ -29,19 +30,66 @@
|
|
|
29
30
|
|
|
30
31
|
template<typename T> class unsafe_pod_vector;
|
|
31
32
|
|
|
33
|
+
// The first inline_capacity elements live inside the object itself; the
|
|
34
|
+
// store moves to the heap only when the vector outgrows them, and stays there.
|
|
35
|
+
// Most vectors in the library (a marginal's configurations, probabilities and
|
|
36
|
+
// masses, a layer's fringe) never get past a handful of elements, so they
|
|
37
|
+
// cost no allocation at all. The flip side: unlike a plain heap vector, a move
|
|
38
|
+
// or swap of a vector that is still inline copies its elements, so pointers
|
|
39
|
+
// into data() do not survive either. (unsafe_pod_vector, below, has no inline
|
|
40
|
+
// storage: it must stay trivially copyable, and a self-pointer would not.)
|
|
32
41
|
template<typename T> class pod_vector
|
|
33
42
|
{
|
|
43
|
+
public:
|
|
44
|
+
static constexpr size_t inline_capacity = 16;
|
|
45
|
+
|
|
46
|
+
private:
|
|
34
47
|
T* backend_past_end;
|
|
35
48
|
T* first_free;
|
|
36
49
|
T* store;
|
|
50
|
+
alignas(T) unsigned char inline_buf[inline_capacity * sizeof(T)];
|
|
51
|
+
|
|
52
|
+
ISOSPEC_FORCE_INLINE T* inline_store() noexcept { return reinterpret_cast<T*>(inline_buf); }
|
|
53
|
+
ISOSPEC_FORCE_INLINE bool is_inline() const noexcept { return store == reinterpret_cast<const T*>(inline_buf); }
|
|
54
|
+
|
|
55
|
+
void reset_to_inline() noexcept
|
|
56
|
+
{
|
|
57
|
+
store = first_free = inline_store();
|
|
58
|
+
backend_past_end = store + inline_capacity;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Takes other's contents; *this must hold no heap store. other is left
|
|
62
|
+
// empty and inline.
|
|
63
|
+
void take(pod_vector<T>& other) noexcept
|
|
64
|
+
{
|
|
65
|
+
if(other.is_inline())
|
|
66
|
+
{
|
|
67
|
+
const size_t n = other.size();
|
|
68
|
+
reset_to_inline();
|
|
69
|
+
memcpy(static_cast<void*>(store), other.store, n * sizeof(T));
|
|
70
|
+
first_free = store + n;
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
{
|
|
74
|
+
backend_past_end = other.backend_past_end;
|
|
75
|
+
first_free = other.first_free;
|
|
76
|
+
store = other.store;
|
|
77
|
+
}
|
|
78
|
+
other.reset_to_inline();
|
|
79
|
+
}
|
|
37
80
|
|
|
38
81
|
public:
|
|
39
|
-
explicit pod_vector(size_t initial_size =
|
|
82
|
+
explicit pod_vector(size_t initial_size = inline_capacity)
|
|
40
83
|
{
|
|
41
84
|
#if !ISOSPEC_BUILDING_R
|
|
42
85
|
static_assert(std::is_trivially_copyable<T>::value, "Cannot use a pod_vector with a non-Plain Old Data type.");
|
|
43
86
|
#endif
|
|
44
87
|
|
|
88
|
+
if(initial_size <= inline_capacity)
|
|
89
|
+
{
|
|
90
|
+
reset_to_inline();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
45
93
|
store = reinterpret_cast<T*>(malloc(sizeof(T) * initial_size));
|
|
46
94
|
if(store == NULL)
|
|
47
95
|
throw std::bad_alloc();
|
|
@@ -51,25 +99,20 @@ template<typename T> class pod_vector
|
|
|
51
99
|
|
|
52
100
|
pod_vector(const pod_vector<T>& other) = delete;
|
|
53
101
|
pod_vector& operator=(const pod_vector<T>& other) = delete;
|
|
54
|
-
pod_vector& operator=(pod_vector<T>&& other)
|
|
102
|
+
pod_vector& operator=(pod_vector<T>&& other) noexcept
|
|
55
103
|
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
if(this != &other)
|
|
105
|
+
{
|
|
106
|
+
if(!is_inline())
|
|
107
|
+
free(store);
|
|
108
|
+
take(other);
|
|
109
|
+
}
|
|
61
110
|
return *this;
|
|
62
111
|
}
|
|
63
112
|
|
|
64
|
-
pod_vector(pod_vector<T>&& other)
|
|
65
|
-
{
|
|
66
|
-
backend_past_end = other.backend_past_end;
|
|
67
|
-
first_free = other.first_free;
|
|
68
|
-
store = other.store;
|
|
69
|
-
other.backend_past_end = other.first_free = other.store = NULL;
|
|
70
|
-
}
|
|
113
|
+
pod_vector(pod_vector<T>&& other) noexcept { take(other); }
|
|
71
114
|
|
|
72
|
-
~pod_vector() { free(store);
|
|
115
|
+
~pod_vector() { if(!is_inline()) free(store); }
|
|
73
116
|
|
|
74
117
|
explicit pod_vector(unsafe_pod_vector<T>&& other)
|
|
75
118
|
{
|
|
@@ -83,9 +126,20 @@ template<typename T> class pod_vector
|
|
|
83
126
|
{
|
|
84
127
|
ISOSPEC_IMPOSSIBLE(n < static_cast<size_t>(backend_past_end - store));
|
|
85
128
|
const std::ptrdiff_t store_used_size = first_free - store;
|
|
86
|
-
T* new_store
|
|
87
|
-
if(
|
|
88
|
-
|
|
129
|
+
T* new_store;
|
|
130
|
+
if(is_inline())
|
|
131
|
+
{
|
|
132
|
+
new_store = reinterpret_cast<T*>(malloc(n * sizeof(T)));
|
|
133
|
+
if(new_store == NULL)
|
|
134
|
+
throw std::bad_alloc();
|
|
135
|
+
memcpy(static_cast<void*>(new_store), store, store_used_size * sizeof(T));
|
|
136
|
+
}
|
|
137
|
+
else
|
|
138
|
+
{
|
|
139
|
+
new_store = reinterpret_cast<T*>(realloc(store, n * sizeof(T)));
|
|
140
|
+
if(new_store == NULL)
|
|
141
|
+
throw std::bad_alloc();
|
|
142
|
+
}
|
|
89
143
|
first_free = new_store + store_used_size;
|
|
90
144
|
backend_past_end = new_store + n;
|
|
91
145
|
store = new_store;
|
|
@@ -187,9 +241,16 @@ template<typename T> class pod_vector
|
|
|
187
241
|
|
|
188
242
|
void swap(pod_vector<T>& other) noexcept
|
|
189
243
|
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
244
|
+
if(!is_inline() && !other.is_inline())
|
|
245
|
+
{
|
|
246
|
+
std::swap(backend_past_end, other.backend_past_end);
|
|
247
|
+
std::swap(first_free, other.first_free);
|
|
248
|
+
std::swap(store, other.store);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
pod_vector<T> tmp(std::move(other));
|
|
252
|
+
other.take(*this);
|
|
253
|
+
take(tmp);
|
|
193
254
|
}
|
|
194
255
|
|
|
195
256
|
typedef T* iterator;
|
|
@@ -214,10 +275,14 @@ template<typename T> class pod_vector
|
|
|
214
275
|
|
|
215
276
|
void clear()
|
|
216
277
|
{
|
|
217
|
-
|
|
218
|
-
|
|
278
|
+
if(!is_inline())
|
|
279
|
+
free(store);
|
|
280
|
+
reset_to_inline();
|
|
219
281
|
}
|
|
220
282
|
|
|
283
|
+
// Unlike clear(), keeps the storage for reuse.
|
|
284
|
+
void clear_keep_capacity() noexcept { first_free = store; }
|
|
285
|
+
|
|
221
286
|
friend class unsafe_pod_vector<T>;
|
|
222
287
|
};
|
|
223
288
|
|
|
@@ -506,6 +506,12 @@ class Iso(object):
|
|
|
506
506
|
def parse_conf(self, cptr, starting_with = 0):
|
|
507
507
|
return tuple(tuple(cptr[i+starting_with] for i in o) for o in self.offsets)
|
|
508
508
|
|
|
509
|
+
def _hand_over(self):
|
|
510
|
+
"""Gives the C Iso away to a *Consuming setup call, which deletes it,
|
|
511
|
+
and leaves this object without one. Saves that call copying it."""
|
|
512
|
+
c_iso, self.iso = self.iso, None
|
|
513
|
+
return c_iso
|
|
514
|
+
|
|
509
515
|
def _get_parse_conf_fun(self):
|
|
510
516
|
# Can't just use the above function as lambda, as the entire class instance will be in closure
|
|
511
517
|
offsets = self.offsets
|
|
@@ -894,7 +900,7 @@ def IsoThreshold(threshold,
|
|
|
894
900
|
**kwds: named arguments to IsoSpectrum.
|
|
895
901
|
"""
|
|
896
902
|
iso = Iso(formula = formula, get_confs = get_confs, **kwargs)
|
|
897
|
-
tabulator = _check_pointer(isoFFI.clib.
|
|
903
|
+
tabulator = _check_pointer(isoFFI.clib.setupThresholdFixedEnvelopeConsuming(iso._hand_over(), threshold, absolute, get_confs),
|
|
898
904
|
"setupThresholdFixedEnvelope")
|
|
899
905
|
ido = IsoDistribution(cobject = tabulator, get_confs = get_confs, iso = iso)
|
|
900
906
|
isoFFI.clib.deleteFixedEnvelope(tabulator, False)
|
|
@@ -916,7 +922,7 @@ def IsoTotalProb(prob_to_cover,
|
|
|
916
922
|
**kwargs: named arguments to the superclass.
|
|
917
923
|
"""
|
|
918
924
|
iso = Iso(formula=formula, get_confs=get_confs, **kwargs)
|
|
919
|
-
tabulator = _check_pointer(isoFFI.clib.
|
|
925
|
+
tabulator = _check_pointer(isoFFI.clib.setupTotalProbFixedEnvelopeConsuming(iso._hand_over(), prob_to_cover, get_minimal_pset, get_confs),
|
|
920
926
|
"setupTotalProbFixedEnvelope")
|
|
921
927
|
ido = IsoDistribution(cobject = tabulator, get_confs = get_confs, iso = iso)
|
|
922
928
|
isoFFI.clib.deleteFixedEnvelope(tabulator, False)
|
|
@@ -947,9 +953,9 @@ def IsoStochastic(no_molecules,
|
|
|
947
953
|
"""
|
|
948
954
|
iso = Iso(formula=formula, get_confs=get_confs, **kwargs)
|
|
949
955
|
if seed is None:
|
|
950
|
-
tabulator = isoFFI.clib.
|
|
956
|
+
tabulator = isoFFI.clib.setupStochasticFixedEnvelopeConsuming(iso._hand_over(), no_molecules, precision, beta_bias, get_confs)
|
|
951
957
|
else:
|
|
952
|
-
tabulator = isoFFI.clib.
|
|
958
|
+
tabulator = isoFFI.clib.setupStochasticFixedEnvelopeSeededConsuming(iso._hand_over(), no_molecules, precision, beta_bias,
|
|
953
959
|
get_confs, _check_seed(seed))
|
|
954
960
|
_check_pointer(tabulator, "setupStochasticFixedEnvelope")
|
|
955
961
|
ido = IsoDistribution(cobject = tabulator, get_confs = get_confs, iso = iso)
|
|
@@ -973,7 +979,7 @@ def IsoBinned(bin_width,
|
|
|
973
979
|
**kwargs: named arguments to the superclass.
|
|
974
980
|
"""
|
|
975
981
|
iso = Iso(formula=formula, get_confs=False, **kwargs)
|
|
976
|
-
tabulator = _check_pointer(isoFFI.clib.
|
|
982
|
+
tabulator = _check_pointer(isoFFI.clib.setupBinnedFixedEnvelopeConsuming(iso._hand_over(), target_total_prob, bin_width, bin_middle),
|
|
977
983
|
"setupBinnedFixedEnvelope")
|
|
978
984
|
ido = IsoDistribution(cobject = tabulator, get_confs = False, iso = iso)
|
|
979
985
|
isoFFI.clib.deleteFixedEnvelope(tabulator, False)
|
|
@@ -92,17 +92,30 @@ class IsoFFI:
|
|
|
92
92
|
double threshold,
|
|
93
93
|
bool absolute,
|
|
94
94
|
bool get_confs);
|
|
95
|
+
void* setupThresholdFixedEnvelopeConsuming(void* iso,
|
|
96
|
+
double threshold,
|
|
97
|
+
bool absolute,
|
|
98
|
+
bool get_confs);
|
|
95
99
|
|
|
96
100
|
void* setupTotalProbFixedEnvelope(void* iso,
|
|
97
101
|
double taget_coverage,
|
|
98
102
|
bool optimize,
|
|
99
103
|
bool get_confs);
|
|
104
|
+
void* setupTotalProbFixedEnvelopeConsuming(void* iso,
|
|
105
|
+
double taget_coverage,
|
|
106
|
+
bool optimize,
|
|
107
|
+
bool get_confs);
|
|
100
108
|
|
|
101
109
|
void* setupStochasticFixedEnvelope(void* iso,
|
|
102
110
|
size_t no_molecules,
|
|
103
111
|
double precision,
|
|
104
112
|
double beta_bias,
|
|
105
113
|
bool get_confs);
|
|
114
|
+
void* setupStochasticFixedEnvelopeConsuming(void* iso,
|
|
115
|
+
size_t no_molecules,
|
|
116
|
+
double precision,
|
|
117
|
+
double beta_bias,
|
|
118
|
+
bool get_confs);
|
|
106
119
|
|
|
107
120
|
void* setupStochasticFixedEnvelopeSeeded(void* iso,
|
|
108
121
|
size_t no_molecules,
|
|
@@ -110,11 +123,21 @@ class IsoFFI:
|
|
|
110
123
|
double beta_bias,
|
|
111
124
|
bool get_confs,
|
|
112
125
|
uint64_t seed);
|
|
126
|
+
void* setupStochasticFixedEnvelopeSeededConsuming(void* iso,
|
|
127
|
+
size_t no_molecules,
|
|
128
|
+
double precision,
|
|
129
|
+
double beta_bias,
|
|
130
|
+
bool get_confs,
|
|
131
|
+
uint64_t seed);
|
|
113
132
|
|
|
114
133
|
void* setupBinnedFixedEnvelope(void* iso,
|
|
115
134
|
double target_total_prob,
|
|
116
135
|
double bin_width,
|
|
117
136
|
double bin_middle);
|
|
137
|
+
void* setupBinnedFixedEnvelopeConsuming(void* iso,
|
|
138
|
+
double target_total_prob,
|
|
139
|
+
double bin_width,
|
|
140
|
+
double bin_middle);
|
|
118
141
|
|
|
119
142
|
void* setupFixedEnvelope(double* masses, double* probs, size_t size, bool mass_sorted, bool prob_sorted, double total_prob);
|
|
120
143
|
void* setupFixedEnvelopeWithConfs(double* masses, double* probs, int* confs, size_t size, int allDim, bool mass_sorted, bool prob_sorted, double total_prob);
|
isospecpy-2.6.0/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.6.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|