IsoSpecPy 2.5.0__py3-none-win_amd64.whl
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/Advanced.py +32 -0
- IsoSpecPy/Distributions.py +93 -0
- IsoSpecPy/Formulas.py +71 -0
- IsoSpecPy/IsoSpec++/aligned_ptr.h +638 -0
- IsoSpecPy/IsoSpec++/allocator.h +69 -0
- IsoSpecPy/IsoSpec++/btrd.h +206 -0
- IsoSpecPy/IsoSpec++/composition.h +126 -0
- IsoSpecPy/IsoSpec++/conf.h +38 -0
- IsoSpecPy/IsoSpec++/cwrapper.h +271 -0
- IsoSpecPy/IsoSpec++/dirtyAllocator.h +58 -0
- IsoSpecPy/IsoSpec++/element_lookup.h +36 -0
- IsoSpecPy/IsoSpec++/element_tables.h +50 -0
- IsoSpecPy/IsoSpec++/fasta.h +70 -0
- IsoSpecPy/IsoSpec++/fasta_mods.h +70 -0
- IsoSpecPy/IsoSpec++/fixedEnvelopes.h +418 -0
- IsoSpecPy/IsoSpec++/isa_kernel_impl.h +192 -0
- IsoSpecPy/IsoSpec++/isa_kernels.h +172 -0
- IsoSpecPy/IsoSpec++/isoMath.h +121 -0
- IsoSpecPy/IsoSpec++/isoSpec++.h +801 -0
- IsoSpecPy/IsoSpec++/iso_simd.h +48 -0
- IsoSpecPy/IsoSpec++/marginalTrek++.h +614 -0
- IsoSpecPy/IsoSpec++/misc.h +229 -0
- IsoSpecPy/IsoSpec++/mman.h +67 -0
- IsoSpecPy/IsoSpec++/operators.h +150 -0
- IsoSpecPy/IsoSpec++/platform.h +180 -0
- IsoSpecPy/IsoSpec++/platform_incl.h +33 -0
- IsoSpecPy/IsoSpec++/pod_vector.h +400 -0
- IsoSpecPy/IsoSpec++/summator.h +118 -0
- IsoSpecPy/IsoSpec++/unimod.h +80 -0
- IsoSpecPy/IsoSpec++/unimod_support.h +2169 -0
- IsoSpecPy/IsoSpec++/unimod_table_data.h +1027 -0
- IsoSpecPy/IsoSpecPy.py +1088 -0
- IsoSpecPy/IsoSpecPyOld.py +290 -0
- IsoSpecPy/PeriodicTbl.py +51 -0
- IsoSpecPy/__init__.py +19 -0
- IsoSpecPy/__main__.py +25 -0
- IsoSpecPy/approximations.py +122 -0
- IsoSpecPy/confs_passthrough.py +16 -0
- IsoSpecPy/isoFFI.py +290 -0
- bin/IsoSpecCppPy.dll +0 -0
- isospecpy-2.5.0.dist-info/METADATA +220 -0
- isospecpy-2.5.0.dist-info/RECORD +45 -0
- isospecpy-2.5.0.dist-info/WHEEL +5 -0
- isospecpy-2.5.0.dist-info/licenses/LICENCE +35 -0
- lib/IsoSpecCppPy.lib +0 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2015-2020 Mateusz Łącki and Michał Startek.
|
|
3
|
+
*
|
|
4
|
+
* This file is part of IsoSpec.
|
|
5
|
+
*
|
|
6
|
+
* IsoSpec is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the Simplified ("2-clause") BSD licence.
|
|
8
|
+
*
|
|
9
|
+
* IsoSpec is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
|
+
*
|
|
13
|
+
* You should have received a copy of the Simplified BSD Licence
|
|
14
|
+
* along with IsoSpec. If not, see <https://opensource.org/licenses/BSD-2-Clause>.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#pragma once
|
|
18
|
+
|
|
19
|
+
#include <cstdlib>
|
|
20
|
+
#include <cstring>
|
|
21
|
+
#include <algorithm>
|
|
22
|
+
#include <vector>
|
|
23
|
+
#include <utility>
|
|
24
|
+
#include <tuple>
|
|
25
|
+
|
|
26
|
+
#include "aligned_ptr.h"
|
|
27
|
+
#include "isoSpec++.h"
|
|
28
|
+
|
|
29
|
+
#ifdef DEBUG
|
|
30
|
+
#define ISOSPEC_INIT_TABLE_SIZE 16
|
|
31
|
+
#else
|
|
32
|
+
#define ISOSPEC_INIT_TABLE_SIZE 1024
|
|
33
|
+
#endif
|
|
34
|
+
|
|
35
|
+
// Widest layer step Binned() will take. total_prob_init uses -5.0, but it trims
|
|
36
|
+
// afterwards so overshoot is harmless there; Binned keeps everything it
|
|
37
|
+
// generates, so a step that overshoots the target is pure waste. -3.0 measured
|
|
38
|
+
// as a uniform ~16-22% win over the fixed -2.0 step advanceToNextConfiguration()
|
|
39
|
+
// would take, across small and large molecules with no overshoot, whereas -5.0
|
|
40
|
+
// regressed on large ones. Overridable at build time for retuning; declared
|
|
41
|
+
// here rather than in the .cpp so the tests can mirror the layer stepping.
|
|
42
|
+
#ifndef ISOSPEC_BINNED_LAYER_MAXSTEP
|
|
43
|
+
# define ISOSPEC_BINNED_LAYER_MAXSTEP (-3.0)
|
|
44
|
+
#endif
|
|
45
|
+
|
|
46
|
+
// The same knob for total_prob_init, which can afford a wider step than Binned:
|
|
47
|
+
// it trims afterwards, so overshooting the target costs only the work of
|
|
48
|
+
// generating what gets thrown away.
|
|
49
|
+
#ifndef ISOSPEC_TOTALPROB_LAYER_MAXSTEP
|
|
50
|
+
# define ISOSPEC_TOTALPROB_LAYER_MAXSTEP (-5.0)
|
|
51
|
+
#endif
|
|
52
|
+
|
|
53
|
+
namespace IsoSpec
|
|
54
|
+
{
|
|
55
|
+
|
|
56
|
+
class FixedEnvelope;
|
|
57
|
+
|
|
58
|
+
//! One of FixedEnvelope's arrays, handed over to the caller together with the
|
|
59
|
+
//! means of freeing it.
|
|
60
|
+
/*!
|
|
61
|
+
release_masses() and friends promise a pointer that plain free() accepts,
|
|
62
|
+
which costs a copy whenever the array is not actually a malloc()'d one (see
|
|
63
|
+
aligned_unique_ptr::release()). These are the copy-free counterpart: the
|
|
64
|
+
caller must invoke deleter(ptr, size) exactly once, and nothing else --
|
|
65
|
+
in particular not free(), unless the deleter happens to be that.
|
|
66
|
+
*/
|
|
67
|
+
struct ISOSPEC_EXPORT_SYMBOL ReleasedArray {
|
|
68
|
+
void* ptr; /*!< nullptr if the envelope had no such array. */
|
|
69
|
+
std::size_t size; /*!< Byte count the deleter expects; meaningless on its own. */
|
|
70
|
+
void (*deleter)(void*, std::size_t) noexcept; /*!< Never null when ptr isn't. */
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
class ISOSPEC_EXPORT_SYMBOL FixedEnvelope {
|
|
74
|
+
protected:
|
|
75
|
+
double* _masses;
|
|
76
|
+
double* _probs;
|
|
77
|
+
int* _confs;
|
|
78
|
+
size_t _confs_no;
|
|
79
|
+
int allDim;
|
|
80
|
+
bool sorted_by_mass;
|
|
81
|
+
bool sorted_by_prob;
|
|
82
|
+
double total_prob;
|
|
83
|
+
size_t current_size;
|
|
84
|
+
double* tmasses;
|
|
85
|
+
double* tprobs;
|
|
86
|
+
int* tconfs;
|
|
87
|
+
int allDimSizeofInt;
|
|
88
|
+
|
|
89
|
+
// Ownership records for the three arrays above. The arrays themselves are
|
|
90
|
+
// always reached through the plain pointers, so every hot loop indexes them
|
|
91
|
+
// directly with no branch; these only say how each one has to be given back.
|
|
92
|
+
// An array comes from exactly one of two places, and the two free differently:
|
|
93
|
+
// * allocated by us -- the matching aligned_unique_ptr is engaged and holds
|
|
94
|
+
// it. Always DOUBLE_SIMD_ALIGNMENT-aligned, so the SIMD fills can store
|
|
95
|
+
// to it; a buffer that grows repeatedly also becomes VM-backed past
|
|
96
|
+
// ISOSPEC_ALIGNED_PTR_VM_THRESHOLD, so growing it stops copying.
|
|
97
|
+
// * adopted across the C ABI -- the aligned_unique_ptr is empty and the
|
|
98
|
+
// plain pointer is a malloc()'d buffer somebody else built (a cffi array
|
|
99
|
+
// taken over zero-copy). Arbitrarily aligned; goes back to free().
|
|
100
|
+
// Invariant: the owner is engaged iff the pointer beside it is ours, and
|
|
101
|
+
// then the two are equal.
|
|
102
|
+
aligned_unique_ptr<double, DOUBLE_SIMD_ALIGNMENT> _masses_owner;
|
|
103
|
+
aligned_unique_ptr<double, DOUBLE_SIMD_ALIGNMENT> _probs_owner;
|
|
104
|
+
aligned_unique_ptr<int, DOUBLE_SIMD_ALIGNMENT> _confs_owner;
|
|
105
|
+
|
|
106
|
+
static void free_deleter(void* p, std::size_t) noexcept { free(p); }
|
|
107
|
+
|
|
108
|
+
//! Free whichever of the two backends holds ptr, and null it.
|
|
109
|
+
template<typename T, std::size_t A>
|
|
110
|
+
static void free_array(aligned_unique_ptr<T, A>& owner, T*& ptr) noexcept
|
|
111
|
+
{
|
|
112
|
+
if(owner)
|
|
113
|
+
owner.reset(0);
|
|
114
|
+
else
|
|
115
|
+
free(ptr);
|
|
116
|
+
ptr = nullptr;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//! Hand ptr back as something free() accepts, copying only if it has to.
|
|
120
|
+
template<typename T, std::size_t A>
|
|
121
|
+
static T* release_array(aligned_unique_ptr<T, A>& owner, T*& ptr)
|
|
122
|
+
{
|
|
123
|
+
T* ret = owner ? owner.release() : ptr;
|
|
124
|
+
ptr = nullptr;
|
|
125
|
+
return ret;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
//! Hand ptr back with its deleter. Never copies.
|
|
129
|
+
template<typename T, std::size_t A>
|
|
130
|
+
static ReleasedArray release_array_with_deleter(aligned_unique_ptr<T, A>& owner, T*& ptr) noexcept
|
|
131
|
+
{
|
|
132
|
+
ReleasedArray ret;
|
|
133
|
+
if(owner)
|
|
134
|
+
{
|
|
135
|
+
typename aligned_unique_ptr<T, A>::release_result r = owner.release_with_deleter();
|
|
136
|
+
ret = ReleasedArray{static_cast<void*>(r.ptr), r.size, r.deleter};
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
ret = ReleasedArray{static_cast<void*>(ptr), 0, &free_deleter};
|
|
140
|
+
ptr = nullptr;
|
|
141
|
+
return ret;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//! Forget ptr without freeing it -- whoever asked for this now owns it.
|
|
145
|
+
template<typename T, std::size_t A>
|
|
146
|
+
static void disown_array(aligned_unique_ptr<T, A>& owner, T*& ptr) noexcept
|
|
147
|
+
{
|
|
148
|
+
if(owner)
|
|
149
|
+
(void)owner.release_with_deleter(); // discards the deleter, but never copies
|
|
150
|
+
ptr = nullptr;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//! Allocate new_n elements, keeping nothing. See ISOSPEC_ALIGNED_PTR_VM_THRESHOLD
|
|
154
|
+
//! for why a one-shot allocation of a known size is not the same operation
|
|
155
|
+
//! as growing into one.
|
|
156
|
+
template<typename T, std::size_t A>
|
|
157
|
+
static void fresh_array(aligned_unique_ptr<T, A>& owner, T*& ptr, size_t new_n)
|
|
158
|
+
{
|
|
159
|
+
if(!owner && ptr != nullptr)
|
|
160
|
+
{
|
|
161
|
+
free(ptr);
|
|
162
|
+
ptr = nullptr;
|
|
163
|
+
}
|
|
164
|
+
owner.reset(new_n);
|
|
165
|
+
ptr = owner.get();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//! Resize to new_n elements, preserving min(old_n, new_n) of them.
|
|
169
|
+
template<typename T, std::size_t A>
|
|
170
|
+
static void grow_array(aligned_unique_ptr<T, A>& owner, T*& ptr, size_t new_n, size_t old_n)
|
|
171
|
+
{
|
|
172
|
+
if(!owner && ptr != nullptr)
|
|
173
|
+
{
|
|
174
|
+
// An adopted buffer did not come from the aligned allocator and so
|
|
175
|
+
// cannot be handed to it for resizing: move the contents across into
|
|
176
|
+
// one that did, then give the original back to free(). Only reachable
|
|
177
|
+
// if somebody grows an envelope built from foreign arrays.
|
|
178
|
+
aligned_unique_ptr<T, A> fresh;
|
|
179
|
+
fresh.realloc(new_n);
|
|
180
|
+
const size_t keep = (std::min)(old_n, new_n);
|
|
181
|
+
if(keep > 0)
|
|
182
|
+
memcpy(fresh.get(), ptr, sizeof(T) * keep);
|
|
183
|
+
free(ptr);
|
|
184
|
+
owner = std::move(fresh);
|
|
185
|
+
}
|
|
186
|
+
else
|
|
187
|
+
// Self-allocated: for a VM-backed buffer this is an in-place remap
|
|
188
|
+
// rather than a copy.
|
|
189
|
+
owner.realloc(new_n);
|
|
190
|
+
ptr = owner.get();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public:
|
|
194
|
+
ISOSPEC_FORCE_INLINE FixedEnvelope() : _masses(nullptr),
|
|
195
|
+
_probs(nullptr),
|
|
196
|
+
_confs(nullptr),
|
|
197
|
+
_confs_no(0),
|
|
198
|
+
allDim(0),
|
|
199
|
+
sorted_by_mass(false),
|
|
200
|
+
sorted_by_prob(false),
|
|
201
|
+
total_prob(NAN),
|
|
202
|
+
current_size(0),
|
|
203
|
+
allDimSizeofInt(0)
|
|
204
|
+
// Deliberately not initializing tmasses, tprobs, tconfs
|
|
205
|
+
{};
|
|
206
|
+
|
|
207
|
+
FixedEnvelope(const FixedEnvelope& other);
|
|
208
|
+
FixedEnvelope(FixedEnvelope&& other);
|
|
209
|
+
FixedEnvelope& operator=(const FixedEnvelope& other);
|
|
210
|
+
FixedEnvelope& operator=(FixedEnvelope&& other);
|
|
211
|
+
|
|
212
|
+
//! Adopt foreign, malloc()'d arrays (see the ownership note above).
|
|
213
|
+
FixedEnvelope(double* masses, double* probs, size_t confs_no, bool masses_sorted = false, bool probs_sorted = false, double _total_prob = NAN);
|
|
214
|
+
FixedEnvelope(double* masses, double* probs, int* confs, size_t confs_no, int _allDim, bool masses_sorted = false, bool probs_sorted = false, double _total_prob = NAN);
|
|
215
|
+
|
|
216
|
+
//! Internal counterpart to the two adopting constructors above: takes over
|
|
217
|
+
//! arrays this library allocated itself, so that everything the library
|
|
218
|
+
//! produces stays aligned instead of being demoted to a plain malloc()'d,
|
|
219
|
+
//! foreign buffer.
|
|
220
|
+
FixedEnvelope(aligned_unique_ptr<double, DOUBLE_SIMD_ALIGNMENT>&& masses,
|
|
221
|
+
aligned_unique_ptr<double, DOUBLE_SIMD_ALIGNMENT>&& probs,
|
|
222
|
+
size_t confs_no, bool masses_sorted = false, bool probs_sorted = false, double _total_prob = NAN);
|
|
223
|
+
|
|
224
|
+
virtual ~FixedEnvelope()
|
|
225
|
+
{
|
|
226
|
+
free_array(_masses_owner, _masses);
|
|
227
|
+
free_array(_probs_owner, _probs);
|
|
228
|
+
free_array(_confs_owner, _confs);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
FixedEnvelope operator+(const FixedEnvelope& other) const;
|
|
232
|
+
FixedEnvelope operator*(const FixedEnvelope& other) const;
|
|
233
|
+
|
|
234
|
+
inline size_t confs_no() const { return _confs_no; }
|
|
235
|
+
inline int getAllDim() const { return allDim; }
|
|
236
|
+
|
|
237
|
+
inline const double* masses() const { return _masses; }
|
|
238
|
+
inline const double* probs() const { return _probs; }
|
|
239
|
+
inline const int* confs() const { return _confs; }
|
|
240
|
+
|
|
241
|
+
//! Hand an array over as a pointer the caller must free() itself.
|
|
242
|
+
/*! Copies the array first if it is not already a malloc()'d one -- see
|
|
243
|
+
ReleasedArray and the release_*_with_deleter() overloads below for the
|
|
244
|
+
variant that never copies. */
|
|
245
|
+
inline double* release_masses() { return release_array(_masses_owner, _masses); }
|
|
246
|
+
inline double* release_probs() { return release_array(_probs_owner, _probs); }
|
|
247
|
+
inline int* release_confs() { return release_array(_confs_owner, _confs); }
|
|
248
|
+
|
|
249
|
+
//! Hand an array over together with its deleter. Never copies.
|
|
250
|
+
inline ReleasedArray release_masses_with_deleter() { return release_array_with_deleter(_masses_owner, _masses); }
|
|
251
|
+
inline ReleasedArray release_probs_with_deleter() { return release_array_with_deleter(_probs_owner, _probs); }
|
|
252
|
+
inline ReleasedArray release_confs_with_deleter() { return release_array_with_deleter(_confs_owner, _confs); }
|
|
253
|
+
|
|
254
|
+
//! Drop all three arrays without freeing any of them: somebody else owns them.
|
|
255
|
+
inline void release_everything() { disown_array(_masses_owner, _masses); disown_array(_probs_owner, _probs); disown_array(_confs_owner, _confs); }
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
inline double mass(size_t i) const { return _masses[i]; }
|
|
259
|
+
inline double prob(size_t i) const { return _probs[i]; }
|
|
260
|
+
inline const int* conf(size_t i) const { return _confs + i*allDim; }
|
|
261
|
+
|
|
262
|
+
void sort_by_mass();
|
|
263
|
+
void sort_by_prob();
|
|
264
|
+
|
|
265
|
+
double get_total_prob();
|
|
266
|
+
void scale(double factor);
|
|
267
|
+
void normalize();
|
|
268
|
+
void shift_mass(double shift);
|
|
269
|
+
void resample(size_t ionic_current, double beta_bias = 1.0);
|
|
270
|
+
|
|
271
|
+
double empiric_average_mass();
|
|
272
|
+
double empiric_variance();
|
|
273
|
+
double empiric_stddev() { return sqrt(empiric_variance()); }
|
|
274
|
+
|
|
275
|
+
double WassersteinDistance(FixedEnvelope& other);
|
|
276
|
+
double OrientedWassersteinDistance(FixedEnvelope& other);
|
|
277
|
+
double AbyssalWassersteinDistance(FixedEnvelope& other, double abyss_depth, double other_scale = 1.0);
|
|
278
|
+
std::tuple<double, double, double> WassersteinMatch(FixedEnvelope& other, double flow_distance, double other_scale = 1.0);
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
static FixedEnvelope LinearCombination(const std::vector<const FixedEnvelope*>& spectra, const std::vector<double>& intensities);
|
|
282
|
+
static FixedEnvelope LinearCombination(const FixedEnvelope* const * spectra, const double* intensities, size_t size);
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
FixedEnvelope bin(double bin_width = 1.0, double middle = 0.0);
|
|
286
|
+
|
|
287
|
+
private:
|
|
288
|
+
void sort_by(double* order);
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
protected:
|
|
292
|
+
template<typename T, bool tgetConfs> ISOSPEC_FORCE_INLINE void store_conf(const T& generator)
|
|
293
|
+
{
|
|
294
|
+
*tmasses = generator.mass(); tmasses++;
|
|
295
|
+
*tprobs = generator.prob(); tprobs++;
|
|
296
|
+
constexpr_if(tgetConfs) { generator.get_conf_signature(tconfs); tconfs += allDim; }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
ISOSPEC_FORCE_INLINE void store_conf(double _mass, double _prob)
|
|
300
|
+
{
|
|
301
|
+
if(_confs_no == current_size)
|
|
302
|
+
reallocate_memory<false>(current_size*2);
|
|
303
|
+
|
|
304
|
+
*tprobs = _prob;
|
|
305
|
+
*tmasses = _mass;
|
|
306
|
+
tprobs++;
|
|
307
|
+
tmasses++;
|
|
308
|
+
|
|
309
|
+
_confs_no++;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
template<bool tgetConfs> ISOSPEC_FORCE_INLINE void swap(size_t idx1, size_t idx2, ISOSPEC_MAYBE_UNUSED int* conf_swapspace)
|
|
313
|
+
{
|
|
314
|
+
std::swap<double>(this->_probs[idx1], this->_probs[idx2]);
|
|
315
|
+
std::swap<double>(this->_masses[idx1], this->_masses[idx2]);
|
|
316
|
+
constexpr_if(tgetConfs)
|
|
317
|
+
{
|
|
318
|
+
int* c1 = this->_confs + (idx1*this->allDim);
|
|
319
|
+
int* c2 = this->_confs + (idx2*this->allDim);
|
|
320
|
+
memcpy(conf_swapspace, c1, this->allDimSizeofInt);
|
|
321
|
+
memcpy(c1, c2, this->allDimSizeofInt);
|
|
322
|
+
memcpy(c2, conf_swapspace, this->allDimSizeofInt);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
//! Resize the three arrays to hold new_size configurations, keeping the
|
|
327
|
+
//! _confs_no already stored -- the growth path, for fills that do not know
|
|
328
|
+
//! how much they will produce.
|
|
329
|
+
template<bool tgetConfs> void reallocate_memory(size_t new_size);
|
|
330
|
+
|
|
331
|
+
//! Allocate the three arrays for exactly new_size configurations, keeping
|
|
332
|
+
//! nothing -- for fills that counted their output up front.
|
|
333
|
+
template<bool tgetConfs> void allocate_memory(size_t new_size);
|
|
334
|
+
|
|
335
|
+
//! Store every remaining configuration of the generator's current layer.
|
|
336
|
+
template<bool tgetConfs> void store_layer(IsoLayeredGenerator& generator);
|
|
337
|
+
|
|
338
|
+
//! Store configurations of the generator's current layer, adding each one's
|
|
339
|
+
//! probability to prob_so_far, until that reaches target_total_prob or the
|
|
340
|
+
//! layer runs out; returns whether it was reached.
|
|
341
|
+
template<bool tgetConfs> bool store_layer_to_prob(IsoLayeredGenerator& generator,
|
|
342
|
+
double& prob_so_far,
|
|
343
|
+
double target_total_prob);
|
|
344
|
+
|
|
345
|
+
public:
|
|
346
|
+
template<bool tgetConfs> void threshold_init(Iso&& iso, double threshold, bool absolute);
|
|
347
|
+
|
|
348
|
+
template<bool tgetConfs, typename GenType = IsoLayeredGenerator> void addConfILG(const GenType& generator)
|
|
349
|
+
{
|
|
350
|
+
if(this->_confs_no == this->current_size)
|
|
351
|
+
this->template reallocate_memory<tgetConfs>(this->current_size*2);
|
|
352
|
+
|
|
353
|
+
this->template store_conf<GenType, tgetConfs>(generator);
|
|
354
|
+
this->_confs_no++;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
template<bool tgetConfs> void total_prob_init(Iso&& iso, double target_prob, bool trim);
|
|
358
|
+
|
|
359
|
+
static FixedEnvelope FromThreshold(Iso&& iso, double threshold, bool absolute, bool tgetConfs = false)
|
|
360
|
+
{
|
|
361
|
+
FixedEnvelope ret;
|
|
362
|
+
|
|
363
|
+
if(tgetConfs)
|
|
364
|
+
ret.threshold_init<true>(std::move(iso), threshold, absolute);
|
|
365
|
+
else
|
|
366
|
+
ret.threshold_init<false>(std::move(iso), threshold, absolute);
|
|
367
|
+
return ret;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
inline static FixedEnvelope FromThreshold(const Iso& iso, double _threshold, bool _absolute, bool tgetConfs = false)
|
|
371
|
+
{
|
|
372
|
+
return FromThreshold(Iso(iso, true), _threshold, _absolute, tgetConfs);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
static FixedEnvelope FromTotalProb(Iso&& iso, double target_total_prob, bool optimize, bool tgetConfs = false)
|
|
376
|
+
{
|
|
377
|
+
FixedEnvelope ret;
|
|
378
|
+
|
|
379
|
+
if(tgetConfs)
|
|
380
|
+
ret.total_prob_init<true>(std::move(iso), target_total_prob, optimize);
|
|
381
|
+
else
|
|
382
|
+
ret.total_prob_init<false>(std::move(iso), target_total_prob, optimize);
|
|
383
|
+
|
|
384
|
+
return ret;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
inline static FixedEnvelope FromTotalProb(const Iso& iso, double _target_total_prob, bool _optimize, bool tgetConfs = false)
|
|
388
|
+
{
|
|
389
|
+
return FromTotalProb(Iso(iso, true), _target_total_prob, _optimize, tgetConfs);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
template<bool tgetConfs> void stochastic_init(Iso&& iso, size_t _no_molecules, double _precision, double _beta_bias);
|
|
393
|
+
|
|
394
|
+
inline static FixedEnvelope FromStochastic(Iso&& iso, size_t _no_molecules, double _precision = 0.9999, double _beta_bias = 5.0, bool tgetConfs = false)
|
|
395
|
+
{
|
|
396
|
+
FixedEnvelope ret;
|
|
397
|
+
|
|
398
|
+
if(tgetConfs)
|
|
399
|
+
ret.stochastic_init<true>(std::move(iso), _no_molecules, _precision, _beta_bias);
|
|
400
|
+
else
|
|
401
|
+
ret.stochastic_init<false>(std::move(iso), _no_molecules, _precision, _beta_bias);
|
|
402
|
+
|
|
403
|
+
return ret;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
static FixedEnvelope FromStochastic(const Iso& iso, size_t _no_molecules, double _precision = 0.9999, double _beta_bias = 5.0, bool tgetConfs = false)
|
|
407
|
+
{
|
|
408
|
+
return FromStochastic(Iso(iso, true), _no_molecules, _precision, _beta_bias, tgetConfs);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
static FixedEnvelope Binned(Iso&& iso, double target_total_prob, double bin_width, double bin_middle = 0.0);
|
|
412
|
+
static FixedEnvelope Binned(const Iso& iso, double target_total_prob, double bin_width, double bin_middle = 0.0)
|
|
413
|
+
{
|
|
414
|
+
return Binned(Iso(iso, true), target_total_prob, bin_width, bin_middle);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
} // namespace IsoSpec
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2015-2020 Mateusz Łącki and Michał Startek.
|
|
3
|
+
*
|
|
4
|
+
* This file is part of IsoSpec.
|
|
5
|
+
*
|
|
6
|
+
* IsoSpec is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the Simplified ("2-clause") BSD licence.
|
|
8
|
+
*
|
|
9
|
+
* IsoSpec is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
|
+
*
|
|
13
|
+
* You should have received a copy of the Simplified BSD Licence
|
|
14
|
+
* along with IsoSpec. If not, see <https://opensource.org/licenses/BSD-2-Clause>.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// The bodies of the two batched fill kernels, in a form meant to be included
|
|
18
|
+
// once per instruction-set level, by a translation unit compiled with that
|
|
19
|
+
// level's -march. Define ISOSPEC_ISA_NAMESPACE to the namespace to put them in
|
|
20
|
+
// before including this; everything lands there, so several levels can coexist
|
|
21
|
+
// in one binary.
|
|
22
|
+
//
|
|
23
|
+
// Nothing here may be given external linkage except the kernels table at the
|
|
24
|
+
// bottom, and nothing outside <experimental/simd> and this project's POD-only
|
|
25
|
+
// headers may be included -- see the long note in isa_kernels.h for what goes
|
|
26
|
+
// wrong otherwise. This file therefore has NO include guard: it is included
|
|
27
|
+
// several times on purpose, with a different namespace each time.
|
|
28
|
+
|
|
29
|
+
#ifndef ISOSPEC_ISA_NAMESPACE
|
|
30
|
+
#error "define ISOSPEC_ISA_NAMESPACE before including isa_kernel_impl.h"
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
#include <cstddef>
|
|
34
|
+
#include "platform.h" // ISOSPEC_HAS_SIMD, simd_double, simd_ns -- macros and typedefs only
|
|
35
|
+
#include "isa_kernels.h" // the POD interface these implement
|
|
36
|
+
|
|
37
|
+
namespace IsoSpec
|
|
38
|
+
{
|
|
39
|
+
namespace ISOSPEC_ISA_NAMESPACE
|
|
40
|
+
{
|
|
41
|
+
|
|
42
|
+
#if ISOSPEC_HAS_SIMD
|
|
43
|
+
|
|
44
|
+
// Both kernels below walk the run the same way. Each step takes the W positions
|
|
45
|
+
// after the cursor at once: one compare decides that all W are above the cutoff
|
|
46
|
+
// -- sound because lProbs descends, so the highest index guards the rest, and
|
|
47
|
+
// safe to read because of the ISOSPEC_LPROB_GUARDIANS sentinels past the end --
|
|
48
|
+
// and then the marginal's mass and probability tables are read as vectors and
|
|
49
|
+
// combined with the higher marginals' (loop-invariant) partial mass and product.
|
|
50
|
+
//
|
|
51
|
+
// Because the compare only succeeds when the *last* lane is above the cutoff,
|
|
52
|
+
// every lane produced is a real configuration, so the reads from the mass and
|
|
53
|
+
// probability tables, which carry no sentinels, stay in bounds.
|
|
54
|
+
//
|
|
55
|
+
// The output is packed contiguously and the caller's scalar drain moves the
|
|
56
|
+
// cursor by a non-multiple of W, so neither the loads nor the stores can be
|
|
57
|
+
// assumed vector-aligned: element_aligned throughout.
|
|
58
|
+
|
|
59
|
+
static std::size_t fill_run(const double** lProbs_ptr,
|
|
60
|
+
const double* lProbs_start,
|
|
61
|
+
const double* marginal_masses,
|
|
62
|
+
const double* marginal_probs,
|
|
63
|
+
double lcfmsv,
|
|
64
|
+
double partial_mass,
|
|
65
|
+
double partial_prob,
|
|
66
|
+
double* out_masses,
|
|
67
|
+
double* out_probs)
|
|
68
|
+
{
|
|
69
|
+
constexpr std::size_t W = simd_double::size();
|
|
70
|
+
const double* p = *lProbs_ptr;
|
|
71
|
+
std::size_t emitted = 0;
|
|
72
|
+
|
|
73
|
+
const simd_double vpartial_prob(partial_prob);
|
|
74
|
+
const simd_double vpartial_mass(partial_mass);
|
|
75
|
+
|
|
76
|
+
simd_double masses;
|
|
77
|
+
simd_double probs;
|
|
78
|
+
|
|
79
|
+
while(ISOSPEC_LIKELY(*(p + W) >= lcfmsv))
|
|
80
|
+
{
|
|
81
|
+
const std::size_t offset = static_cast<std::size_t>(p - lProbs_start) + 1;
|
|
82
|
+
|
|
83
|
+
probs.copy_from(marginal_probs + offset, simd_ns::element_aligned);
|
|
84
|
+
probs *= vpartial_prob;
|
|
85
|
+
masses.copy_from(marginal_masses + offset, simd_ns::element_aligned);
|
|
86
|
+
masses += vpartial_mass;
|
|
87
|
+
|
|
88
|
+
masses.copy_to(out_masses + emitted, simd_ns::element_aligned);
|
|
89
|
+
probs.copy_to(out_probs + emitted, simd_ns::element_aligned);
|
|
90
|
+
|
|
91
|
+
emitted += W;
|
|
92
|
+
p += W;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
*lProbs_ptr = p;
|
|
96
|
+
return emitted;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static std::size_t bin_run(const double** lProbs_ptr,
|
|
100
|
+
const double* lProbs_start,
|
|
101
|
+
const double* marginal_masses,
|
|
102
|
+
const double* marginal_probs,
|
|
103
|
+
double lcfmsv,
|
|
104
|
+
double partial_mass,
|
|
105
|
+
double partial_prob,
|
|
106
|
+
double* acc,
|
|
107
|
+
double hwmm,
|
|
108
|
+
double inv_bin_width)
|
|
109
|
+
{
|
|
110
|
+
constexpr std::size_t W = simd_double::size();
|
|
111
|
+
const double* p = *lProbs_ptr;
|
|
112
|
+
std::size_t emitted = 0;
|
|
113
|
+
|
|
114
|
+
const simd_double vpartial_prob(partial_prob);
|
|
115
|
+
const simd_double vpartial_mass(partial_mass);
|
|
116
|
+
const simd_double vhwmm(hwmm);
|
|
117
|
+
const simd_double vinv_bin_width(inv_bin_width);
|
|
118
|
+
|
|
119
|
+
simd_double masses;
|
|
120
|
+
simd_double probs;
|
|
121
|
+
|
|
122
|
+
while(ISOSPEC_LIKELY(*(p + W) >= lcfmsv))
|
|
123
|
+
{
|
|
124
|
+
const std::size_t offset = static_cast<std::size_t>(p - lProbs_start) + 1;
|
|
125
|
+
|
|
126
|
+
probs.copy_from(marginal_probs + offset, simd_ns::element_aligned);
|
|
127
|
+
probs *= vpartial_prob;
|
|
128
|
+
masses.copy_from(marginal_masses + offset, simd_ns::element_aligned);
|
|
129
|
+
masses += vpartial_mass;
|
|
130
|
+
|
|
131
|
+
// The vector form of bin_index(): the same arithmetic in the same order,
|
|
132
|
+
// so it lands in the same bin and, like it, stays inside the allocation.
|
|
133
|
+
// The scatter that consumes it cannot be vectorised -- the indices repeat
|
|
134
|
+
// and collide, and resolving that would need AVX-512CD conflict detection
|
|
135
|
+
// -- but everything ahead of it is, which is where the work is.
|
|
136
|
+
const simd_double idx = simd_ns::floor((masses + vhwmm) * vinv_bin_width);
|
|
137
|
+
|
|
138
|
+
// Hand the probabilities to the scatter through a buffer rather than by
|
|
139
|
+
// subscripting the vector. Clang otherwise sees every lane extracted and
|
|
140
|
+
// un-vectorises the multiply above into W scalar vmulsd's, each with its
|
|
141
|
+
// own 8-byte load, putting a load->mul->add->store chain on every lane:
|
|
142
|
+
// measured 2.03x slower on Alder Lake, 1.36x on Piledriver. The spill
|
|
143
|
+
// itself is free -- these values are consumed one lane at a time either
|
|
144
|
+
// way, so the round trip through memory was always going to happen; this
|
|
145
|
+
// only fixes where the multiply lands relative to it. GCC is unaffected
|
|
146
|
+
// (it kept the packed form regardless) and the results are bit-identical.
|
|
147
|
+
alignas(DOUBLE_SIMD_ALIGNMENT) double pbuf[W];
|
|
148
|
+
probs.copy_to(pbuf, simd_ns::vector_aligned);
|
|
149
|
+
|
|
150
|
+
for(std::size_t ii = 0; ii < W; ii++)
|
|
151
|
+
acc[static_cast<std::ptrdiff_t>(idx[ii])] += pbuf[ii];
|
|
152
|
+
|
|
153
|
+
emitted += W;
|
|
154
|
+
p += W;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
*lProbs_ptr = p;
|
|
158
|
+
return emitted;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
constexpr std::size_t kernel_lanes = simd_double::size();
|
|
162
|
+
|
|
163
|
+
#else // no <experimental/simd> on this toolchain
|
|
164
|
+
|
|
165
|
+
// Emit nothing and let the caller's scalar drain do all the work: that drain is
|
|
166
|
+
// a complete implementation on its own, so this needs no scalar duplicate of
|
|
167
|
+
// the loops above.
|
|
168
|
+
|
|
169
|
+
static std::size_t fill_run(const double**, const double*, const double*, const double*,
|
|
170
|
+
double, double, double, double*, double*)
|
|
171
|
+
{
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static std::size_t bin_run(const double**, const double*, const double*, const double*,
|
|
176
|
+
double, double, double, double*, double, double)
|
|
177
|
+
{
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
constexpr std::size_t kernel_lanes = 1;
|
|
182
|
+
|
|
183
|
+
#endif // ISOSPEC_HAS_SIMD
|
|
184
|
+
|
|
185
|
+
// The one symbol with external linkage. `extern` is load-bearing: a const object
|
|
186
|
+
// at namespace scope would otherwise have internal linkage and the whole level
|
|
187
|
+
// would be optimised out of the object file.
|
|
188
|
+
extern const IsaKernels kernels;
|
|
189
|
+
extern const IsaKernels kernels = { &fill_run, &bin_run, ISOSPEC_ISA_NAME, kernel_lanes };
|
|
190
|
+
|
|
191
|
+
} // namespace ISOSPEC_ISA_NAMESPACE
|
|
192
|
+
} // namespace IsoSpec
|