IsoSpecPy 2.3.0.dev11__cp313-cp313-win_arm64.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 +94 -0
- IsoSpecPy/Formulas.py +71 -0
- IsoSpecPy/IsoSpec++/allocator.h +69 -0
- IsoSpecPy/IsoSpec++/btrd.h +206 -0
- IsoSpecPy/IsoSpec++/conf.h +38 -0
- IsoSpecPy/IsoSpec++/cwrapper.h +179 -0
- IsoSpecPy/IsoSpec++/dirtyAllocator.h +58 -0
- IsoSpecPy/IsoSpec++/element_tables.h +50 -0
- IsoSpecPy/IsoSpec++/fasta.h +67 -0
- IsoSpecPy/IsoSpec++/fixedEnvelopes.h +244 -0
- IsoSpecPy/IsoSpec++/isoMath.h +87 -0
- IsoSpecPy/IsoSpec++/isoSpec++.h +693 -0
- IsoSpecPy/IsoSpec++/marginalTrek++.h +573 -0
- IsoSpecPy/IsoSpec++/misc.h +204 -0
- IsoSpecPy/IsoSpec++/mman.h +67 -0
- IsoSpecPy/IsoSpec++/operators.h +150 -0
- IsoSpecPy/IsoSpec++/platform.h +111 -0
- IsoSpecPy/IsoSpec++/platform_incl.h +33 -0
- IsoSpecPy/IsoSpec++/pod_vector.h +399 -0
- IsoSpecPy/IsoSpec++/summator.h +118 -0
- IsoSpecPy/IsoSpecPy.py +840 -0
- IsoSpecPy/IsoSpecPyOld.py +294 -0
- IsoSpecPy/PeriodicTbl.py +38 -0
- IsoSpecPy/__init__.py +19 -0
- IsoSpecPy/__main__.py +25 -0
- IsoSpecPy/approximations.py +131 -0
- IsoSpecPy/confs_passthrough.py +16 -0
- IsoSpecPy/isoFFI.py +223 -0
- bin/IsoSpecCppPy.dll +0 -0
- isospecpy-2.3.0.dev11.dist-info/METADATA +43 -0
- isospecpy-2.3.0.dev11.dist-info/RECORD +35 -0
- isospecpy-2.3.0.dev11.dist-info/WHEEL +5 -0
- isospecpy-2.3.0.dev11.dist-info/licenses/LICENCE +35 -0
- lib/IsoSpecCppPy.lib +0 -0
IsoSpecPy/IsoSpecPy.py
ADDED
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2015-2020 Mateusz Łącki and Michał Startek.
|
|
4
|
+
#
|
|
5
|
+
# This file is part of IsoSpec.
|
|
6
|
+
#
|
|
7
|
+
# IsoSpec is free software: you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the Simplified ("2-clause") BSD licence.
|
|
9
|
+
#
|
|
10
|
+
# IsoSpec is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the Simplified BSD Licence
|
|
15
|
+
# along with IsoSpec. If not, see <https://opensource.org/licenses/BSD-2-Clause>.
|
|
16
|
+
#
|
|
17
|
+
|
|
18
|
+
from .isoFFI import isoFFI
|
|
19
|
+
import re
|
|
20
|
+
import types
|
|
21
|
+
from . import PeriodicTbl
|
|
22
|
+
from .confs_passthrough import ConfsPassthrough
|
|
23
|
+
from collections import namedtuple, OrderedDict
|
|
24
|
+
import math
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
xrange
|
|
28
|
+
except NameError:
|
|
29
|
+
xrange = range
|
|
30
|
+
|
|
31
|
+
regex_pattern = re.compile('([A-Z][a-z]?)(-?[0-9]*)')
|
|
32
|
+
ParsedFormula = namedtuple('ParsedFormula', 'atomCounts masses probs elems')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def ParseFormula(formula):
|
|
38
|
+
"""Parse a chemical formula.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O"
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
A tuple containing element symbols and atomCounts of elements in the parsed formula.
|
|
45
|
+
"""
|
|
46
|
+
global regex_pattern
|
|
47
|
+
|
|
48
|
+
ret = OrderedDict()
|
|
49
|
+
|
|
50
|
+
last = 0
|
|
51
|
+
for match in re.finditer(regex_pattern, formula):
|
|
52
|
+
elem, cnt = match.groups()
|
|
53
|
+
if elem in ret:
|
|
54
|
+
raise ValueError("""Invalid formula: {} (repeating element: "{}")""".format(formula, elem))
|
|
55
|
+
ret[elem] = int(cnt) if cnt != '' else 1
|
|
56
|
+
if last!=match.start():
|
|
57
|
+
raise ValueError("""Invalid formula: {} (garbage inside: "{}")""".format(formula, formula[last:match.start()]))
|
|
58
|
+
if elem not in PeriodicTbl.symbol_to_masses:
|
|
59
|
+
raise ValueError("""Invalid formula: {} (unknown element symbol: "{}")""".format(formula, elem))
|
|
60
|
+
last = match.end()
|
|
61
|
+
|
|
62
|
+
if len(formula) != last:
|
|
63
|
+
raise ValueError('''Invalid formula: {} (trailing garbage: "{}")'''.format(formula, formula[last:]))
|
|
64
|
+
|
|
65
|
+
if len(ret) == 0:
|
|
66
|
+
raise ValueError("Invalid formula (empty)")
|
|
67
|
+
|
|
68
|
+
return ret
|
|
69
|
+
|
|
70
|
+
fasta_parsing_space = isoFFI.ffi.new("int[6]")
|
|
71
|
+
|
|
72
|
+
def ParseFASTA(fasta):
|
|
73
|
+
if isinstance(fasta, str):
|
|
74
|
+
fasta = fasta.encode("ascii")
|
|
75
|
+
isoFFI.clib.parse_fasta_c(fasta, fasta_parsing_space)
|
|
76
|
+
elements = list("CHNOS")
|
|
77
|
+
if fasta_parsing_space[5] > 0:
|
|
78
|
+
elements.append("Se")
|
|
79
|
+
od = OrderedDict()
|
|
80
|
+
for i in range(len(elements)):
|
|
81
|
+
od[elements[i]] = fasta_parsing_space[i]
|
|
82
|
+
return od
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def IsoParamsFromDict(formula, use_nominal_masses = False):
|
|
86
|
+
"""Produces a set of IsoSpec parameters from a chemical formula.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
formula (dict): a parsed chemical formula, e.g. {"C": 2, "H": 6, "O": 1}
|
|
90
|
+
use_nominal_masses (boolean): use masses of elements rounded to integer numbers (nominal masses)
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
ParsedFormula: a tuple containing atomCounts, masses and marginal probabilities of elements in the parsed formula.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
symbols, atomCounts = [], []
|
|
97
|
+
for symbol, atomCount in formula.items():
|
|
98
|
+
symbols.append(symbol)
|
|
99
|
+
atomCounts.append(atomCount)
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
if use_nominal_masses:
|
|
103
|
+
masses = [PeriodicTbl.symbol_to_massNo[s] for s in symbols]
|
|
104
|
+
else:
|
|
105
|
+
masses = [PeriodicTbl.symbol_to_masses[s] for s in symbols]
|
|
106
|
+
probs = [PeriodicTbl.symbol_to_probs[s] for s in symbols]
|
|
107
|
+
except KeyError:
|
|
108
|
+
raise ValueError("Invalid formula")
|
|
109
|
+
|
|
110
|
+
return ParsedFormula(atomCounts, masses, probs, symbols)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def IsoParamsFromFormula(formula, use_nominal_masses = False):
|
|
114
|
+
"""Produces a set of IsoSpec parameters from a chemical formula.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O"
|
|
118
|
+
use_nominal_masses (boolean): use masses of elements rounded to integer numbers (nominal masses)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
ParsedFormula: a tuple containing atomCounts, masses and marginal probabilities of elements in the parsed formula.
|
|
122
|
+
"""
|
|
123
|
+
parsed = ParseFormula(formula)
|
|
124
|
+
return IsoParamsFromDict(parsed, use_nominal_masses = use_nominal_masses)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class Iso(object):
|
|
128
|
+
"""Virtual class representing an isotopic distribution."""
|
|
129
|
+
def __init__(self, formula="",
|
|
130
|
+
get_confs=False,
|
|
131
|
+
atomCounts=None,
|
|
132
|
+
isotopeMasses=None,
|
|
133
|
+
isotopeProbabilities=None,
|
|
134
|
+
use_nominal_masses = False,
|
|
135
|
+
fasta = "",
|
|
136
|
+
charge = 1.0):
|
|
137
|
+
"""Initialize Iso.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
141
|
+
get_confs (boolean): should we report counts of isotopologues?
|
|
142
|
+
atomCounts (list): a list of atom counts (alternative to 'formula').
|
|
143
|
+
isotopeMasses (list): a list of lists of masses of elements with counts in 'atomCounts'.
|
|
144
|
+
isotopeProbabilities (list): a list of lists of probabilities of elements with counts in 'atomCounts'.
|
|
145
|
+
use_nominal_masses (boolean): should the masses be rounded to the closest integer values.
|
|
146
|
+
charge (float): charge state of the molecule: all masses will be divided by this value to obtain the m/z values.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
self.iso = None
|
|
150
|
+
|
|
151
|
+
if len(fasta) > 0:
|
|
152
|
+
molecule = ParseFASTA(fasta)
|
|
153
|
+
else:
|
|
154
|
+
molecule = OrderedDict()
|
|
155
|
+
|
|
156
|
+
if len(formula) > 0:
|
|
157
|
+
if isinstance(formula, dict):
|
|
158
|
+
df = formula
|
|
159
|
+
else:
|
|
160
|
+
df = ParseFormula(formula)
|
|
161
|
+
for symbol, count in df.items():
|
|
162
|
+
molecule[symbol] = molecule.get(symbol, 0) + count
|
|
163
|
+
|
|
164
|
+
for sym, cnt in molecule.items():
|
|
165
|
+
if cnt < 0:
|
|
166
|
+
raise Exception("Negative count of element " + sym + ": " + str(cnt))
|
|
167
|
+
|
|
168
|
+
if len(molecule) == 0 and not all([atomCounts, isotopeMasses, isotopeProbabilities]):
|
|
169
|
+
raise Exception("Either formula, fasta or ALL of: atomCounts, isotopeMasses, isotopeProbabilities must not be None")
|
|
170
|
+
|
|
171
|
+
if len(molecule) > 0:
|
|
172
|
+
self.atomCounts, self.isotopeMasses, self.isotopeProbabilities, _ = IsoParamsFromDict(molecule, use_nominal_masses = use_nominal_masses)
|
|
173
|
+
else:
|
|
174
|
+
self.atomCounts, self.isotopeMasses, self.isotopeProbabilities = [], [], []
|
|
175
|
+
|
|
176
|
+
if not (atomCounts is None):
|
|
177
|
+
self.atomCounts.extend(atomCounts)
|
|
178
|
+
|
|
179
|
+
if not (isotopeMasses is None):
|
|
180
|
+
self.isotopeMasses.extend(isotopeMasses)
|
|
181
|
+
|
|
182
|
+
if not (isotopeProbabilities is None):
|
|
183
|
+
self.isotopeProbabilities.extend(isotopeProbabilities)
|
|
184
|
+
|
|
185
|
+
for sublist in self.isotopeProbabilities:
|
|
186
|
+
for prob in sublist:
|
|
187
|
+
if not (0.0 < prob <= 1.0):
|
|
188
|
+
raise ValueError("All isotope probabilities p must fulfill: 0.0 < p <= 1.0")
|
|
189
|
+
|
|
190
|
+
self.isotopeNumbers = tuple(map(len, self.isotopeMasses))
|
|
191
|
+
assert self.isotopeNumbers == tuple(map(len, self.isotopeProbabilities))
|
|
192
|
+
assert len(self.atomCounts) == len(self.isotopeNumbers) == len(self.isotopeProbabilities)
|
|
193
|
+
|
|
194
|
+
self.dimNumber = len(self.isotopeNumbers)
|
|
195
|
+
|
|
196
|
+
self.get_confs = get_confs
|
|
197
|
+
self.ffi = isoFFI.clib
|
|
198
|
+
|
|
199
|
+
offsets = []
|
|
200
|
+
|
|
201
|
+
if get_confs:
|
|
202
|
+
i = 0
|
|
203
|
+
for j in xrange(self.dimNumber):
|
|
204
|
+
newl = []
|
|
205
|
+
for k in xrange(self.isotopeNumbers[j]):
|
|
206
|
+
newl.append(i)
|
|
207
|
+
i += 1
|
|
208
|
+
offsets.append(tuple(newl))
|
|
209
|
+
self.offsets = tuple(offsets)
|
|
210
|
+
|
|
211
|
+
self.iso = self.ffi.setupIso(self.dimNumber, self.isotopeNumbers,
|
|
212
|
+
self.atomCounts,
|
|
213
|
+
[i/charge for s in self.isotopeMasses for i in s],
|
|
214
|
+
[i for s in self.isotopeProbabilities for i in s])
|
|
215
|
+
|
|
216
|
+
def __del__(self):
|
|
217
|
+
try:
|
|
218
|
+
if self.iso is not None:
|
|
219
|
+
self.ffi.deleteIso(self.iso)
|
|
220
|
+
self.iso = None
|
|
221
|
+
except AttributeError:
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
def getLightestPeakMass(self):
|
|
225
|
+
"""Get the lightest peak in the isotopic distribution."""
|
|
226
|
+
return self.ffi.getLightestPeakMassIso(self.iso)
|
|
227
|
+
|
|
228
|
+
def getHeaviestPeakMass(self):
|
|
229
|
+
"""Get the heaviest peak in the isotopic distribution."""
|
|
230
|
+
return self.ffi.getHeaviestPeakMassIso(self.iso)
|
|
231
|
+
|
|
232
|
+
def getMonoisotopicPeakMass(self):
|
|
233
|
+
"""Get the monoisotopic mass of the peak."""
|
|
234
|
+
return self.ffi.getMonoisotopicPeakMassIso(self.iso)
|
|
235
|
+
|
|
236
|
+
def getModeLProb(self):
|
|
237
|
+
"""Get the log probability of the most probable peak(s) in the isotopic distribution."""
|
|
238
|
+
return self.ffi.getModeLProbIso(self.iso)
|
|
239
|
+
|
|
240
|
+
def getModeMass(self):
|
|
241
|
+
"""Get the mass of the most probable peak.
|
|
242
|
+
|
|
243
|
+
If there are more, return only the mass of one of them."""
|
|
244
|
+
return self.ffi.getModeMassIso(self.iso)
|
|
245
|
+
|
|
246
|
+
def getTheoreticalAverageMass(self):
|
|
247
|
+
return self.ffi.getTheoreticalAverageMassIso(self.iso)
|
|
248
|
+
|
|
249
|
+
def variance(self):
|
|
250
|
+
return self.ffi.getIsoVariance(self.iso)
|
|
251
|
+
|
|
252
|
+
def stddev(self):
|
|
253
|
+
return self.ffi.getIsoStddev(self.iso)
|
|
254
|
+
|
|
255
|
+
def getMarginalLogSizeEstimates(self, prob):
|
|
256
|
+
cbuf = isoFFI.clib.getMarginalLogSizeEstimates(self.iso, prob)
|
|
257
|
+
ret = list(isoFFI.ffi.cast('double[' + str(self.dimNumber) + ']', cbuf))
|
|
258
|
+
isoFFI.clib.freeReleasedArray(cbuf)
|
|
259
|
+
return ret
|
|
260
|
+
|
|
261
|
+
def parse_conf(self, cptr, starting_with = 0):
|
|
262
|
+
return tuple(tuple(cptr[i+starting_with] for i in o) for o in self.offsets)
|
|
263
|
+
|
|
264
|
+
def _get_parse_conf_fun(self):
|
|
265
|
+
# Can't just use the above function as lambda, as the entire class instance will be in closure
|
|
266
|
+
offsets = self.offsets
|
|
267
|
+
def pc(cptr, starting_with = 0):
|
|
268
|
+
return tuple(tuple(cptr[i+starting_with] for i in o) for o in offsets)
|
|
269
|
+
return pc
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class IsoDistribution(object):
|
|
273
|
+
"""Isotopoic distribution with precomputed vector of masses and probabilities."""
|
|
274
|
+
def np_masses(self):
|
|
275
|
+
"""Return computed masses as a numpy array."""
|
|
276
|
+
try:
|
|
277
|
+
import numpy as np
|
|
278
|
+
except ImportError as e:
|
|
279
|
+
raise Exception(e.msg + "\nThis requires numpy to be installed.")
|
|
280
|
+
return np.frombuffer(isoFFI.ffi.buffer(self.masses))
|
|
281
|
+
|
|
282
|
+
def np_probs(self):
|
|
283
|
+
"""Return computed probabilities as a numpy array."""
|
|
284
|
+
try:
|
|
285
|
+
import numpy as np
|
|
286
|
+
except ImportError as e:
|
|
287
|
+
raise Exception(e.msg + "\nThis requires numpy to be installed.")
|
|
288
|
+
return np.frombuffer(isoFFI.ffi.buffer(self.probs))
|
|
289
|
+
|
|
290
|
+
def __iter__(self):
|
|
291
|
+
if hasattr(self, "confs") and self.confs is not None:
|
|
292
|
+
for i in xrange(self.size):
|
|
293
|
+
yield(self.masses[i], self.probs[i], self.confs[i])
|
|
294
|
+
else:
|
|
295
|
+
for i in xrange(self.size):
|
|
296
|
+
yield (self.masses[i], self.probs[i])
|
|
297
|
+
|
|
298
|
+
def __getitem__(self, idx):
|
|
299
|
+
try:
|
|
300
|
+
return (self.masses[idx], self.probs[idx], self.confs[idx])
|
|
301
|
+
except (AttributeError, TypeError):
|
|
302
|
+
return (self.masses[idx], self.probs[idx])
|
|
303
|
+
|
|
304
|
+
def __len__(self):
|
|
305
|
+
"""Get the number of calculated peaks."""
|
|
306
|
+
return self.size
|
|
307
|
+
|
|
308
|
+
def _get_conf(self, idx):
|
|
309
|
+
return self.parse_conf(self.raw_confs, starting_with = self.sum_isotope_numbers * idx)
|
|
310
|
+
|
|
311
|
+
def __del__(self):
|
|
312
|
+
pass
|
|
313
|
+
|
|
314
|
+
def __init__(self, cobject = None, probs = None, masses = None, get_confs = False, iso = None):
|
|
315
|
+
self.mass_sorted = False
|
|
316
|
+
self.prob_sorted = False
|
|
317
|
+
self._total_prob = float('nan')
|
|
318
|
+
|
|
319
|
+
if cobject is not None:
|
|
320
|
+
self.size = isoFFI.clib.confs_noFixedEnvelope(cobject)
|
|
321
|
+
|
|
322
|
+
def wrap(typename, what, attrname, mult = 1):
|
|
323
|
+
if what is not None:
|
|
324
|
+
x = isoFFI.ffi.gc(isoFFI.ffi.cast(typename + '[' + str(self.size*mult) + ']', what), isoFFI.clib.freeReleasedArray)
|
|
325
|
+
setattr(self, attrname, x)
|
|
326
|
+
|
|
327
|
+
wrap("double", isoFFI.clib.massesFixedEnvelope(cobject), "masses")
|
|
328
|
+
wrap("double", isoFFI.clib.probsFixedEnvelope(cobject), "probs")
|
|
329
|
+
|
|
330
|
+
if get_confs:
|
|
331
|
+
# Must also be a subclass of Iso...
|
|
332
|
+
self.sum_isotope_numbers = sum(iso.isotopeNumbers)
|
|
333
|
+
wrap("int", isoFFI.clib.confsFixedEnvelope(cobject), "raw_confs", mult = self.sum_isotope_numbers)
|
|
334
|
+
self.confs = ConfsPassthrough(lambda idx: self._get_conf(idx), self.size)
|
|
335
|
+
self.parse_conf = iso._get_parse_conf_fun()
|
|
336
|
+
|
|
337
|
+
elif probs is not None or masses is not None:
|
|
338
|
+
assert probs is not None and masses is not None
|
|
339
|
+
assert len(probs) == len(masses)
|
|
340
|
+
self.size = len(probs)
|
|
341
|
+
type_str = "double["+str(self.size)+"]"
|
|
342
|
+
self.probs = isoFFI.ffi.new(type_str, probs)
|
|
343
|
+
self.masses = isoFFI.ffi.new(type_str, masses)
|
|
344
|
+
elif cobject == probs == masses == get_confs == iso == None:
|
|
345
|
+
self.size = 0
|
|
346
|
+
type_str = "double["+str(self.size)+"]"
|
|
347
|
+
self.probs = isoFFI.ffi.new(type_str, [])
|
|
348
|
+
self.masses = isoFFI.ffi.new(type_str, [])
|
|
349
|
+
self._total_prob = 0.0
|
|
350
|
+
self.mass_sorted = True
|
|
351
|
+
self.prob_sorted = True
|
|
352
|
+
else:
|
|
353
|
+
raise RuntimeError("Invalid arguments for IsoDistribution constructor")
|
|
354
|
+
|
|
355
|
+
def _get_cobject(self):
|
|
356
|
+
return isoFFI.clib.setupFixedEnvelope(self.masses, self.probs, len(self.masses), self.mass_sorted, self.prob_sorted, self._total_prob)
|
|
357
|
+
|
|
358
|
+
def copy(self):
|
|
359
|
+
x = self._get_cobject()
|
|
360
|
+
c = isoFFI.clib.copyFixedEnvelope(x)
|
|
361
|
+
isoFFI.clib.deleteFixedEnvelope(x, True)
|
|
362
|
+
ret = IsoDistribution(cobject = c)
|
|
363
|
+
ret._total_prob = self._total_prob
|
|
364
|
+
ret.mass_sorted = self.mass_sorted
|
|
365
|
+
ret.prob_sorted = self.prob_sorted
|
|
366
|
+
isoFFI.clib.deleteFixedEnvelope(c, False)
|
|
367
|
+
return ret
|
|
368
|
+
|
|
369
|
+
def __add__(self, other):
|
|
370
|
+
x = self._get_cobject()
|
|
371
|
+
y = other._get_cobject()
|
|
372
|
+
cobject = isoFFI.clib.addEnvelopes(x, y)
|
|
373
|
+
isoFFI.clib.deleteFixedEnvelope(x, True)
|
|
374
|
+
isoFFI.clib.deleteFixedEnvelope(y, True)
|
|
375
|
+
ret = IsoDistribution(cobject = cobject)
|
|
376
|
+
isoFFI.clib.deleteFixedEnvelope(cobject, False)
|
|
377
|
+
return ret
|
|
378
|
+
|
|
379
|
+
def __mul__(self, other):
|
|
380
|
+
x = self._get_cobject()
|
|
381
|
+
y = other._get_cobject()
|
|
382
|
+
cobject = isoFFI.clib.convolveEnvelopes(x, y)
|
|
383
|
+
isoFFI.clib.deleteFixedEnvelope(x, True)
|
|
384
|
+
isoFFI.clib.deleteFixedEnvelope(y, True)
|
|
385
|
+
ret = IsoDistribution(cobject = cobject)
|
|
386
|
+
isoFFI.clib.deleteFixedEnvelope(cobject, False)
|
|
387
|
+
return ret
|
|
388
|
+
|
|
389
|
+
def total_prob(self):
|
|
390
|
+
if math.isnan(self._total_prob):
|
|
391
|
+
co = self._get_cobject()
|
|
392
|
+
self._total_prob = isoFFI.clib.getTotalProbOfEnvelope(co)
|
|
393
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
394
|
+
return self._total_prob
|
|
395
|
+
|
|
396
|
+
def normalize(self):
|
|
397
|
+
co = self._get_cobject()
|
|
398
|
+
isoFFI.clib.normalizeEnvelope(co)
|
|
399
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
400
|
+
self._total_prob = 1.0
|
|
401
|
+
|
|
402
|
+
def normalized(self):
|
|
403
|
+
ret = self.copy()
|
|
404
|
+
ret.normalize()
|
|
405
|
+
return ret
|
|
406
|
+
|
|
407
|
+
def add_mass(self, d_mass):
|
|
408
|
+
isoFFI.clib.array_add(self.masses, self.size, d_mass)
|
|
409
|
+
|
|
410
|
+
def mul_mass(self, d_mass):
|
|
411
|
+
isoFFI.clib.array_mul(self.masses, self.size, d_mass)
|
|
412
|
+
|
|
413
|
+
def add_mul_mass(self, add, mul):
|
|
414
|
+
isoFFI.clib.array_fma(self.masses, self.size, mul, add*mul)
|
|
415
|
+
|
|
416
|
+
def mul_add_mass(self, mul, add):
|
|
417
|
+
isoFFI.clib.array_fma(self.masses, self.size, mul, add)
|
|
418
|
+
|
|
419
|
+
def scale(self, factor):
|
|
420
|
+
'''Multiplies the pribabilities of spectrum by factor. Works in place.'''
|
|
421
|
+
co = self._get_cobject()
|
|
422
|
+
isoFFI.clib.scaleEnvelope(co, factor)
|
|
423
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
424
|
+
self._total_prob *= factor
|
|
425
|
+
|
|
426
|
+
def scaled(self, factor):
|
|
427
|
+
'''Returns a copy of the spectrum where each probability was multiplied by factor.'''
|
|
428
|
+
ret = self.copy()
|
|
429
|
+
ret.scale(factor)
|
|
430
|
+
return ret
|
|
431
|
+
|
|
432
|
+
def resample(self, ionic_current, beta_bias=1.0):
|
|
433
|
+
co = self._get_cobject()
|
|
434
|
+
isoFFI.clib.resampleEnvelope(co, ionic_current, beta_bias)
|
|
435
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
436
|
+
self._total_prob = float(ionic_current)
|
|
437
|
+
self.prob_sorted = False
|
|
438
|
+
|
|
439
|
+
def sort_by_prob(self):
|
|
440
|
+
if not self.prob_sorted:
|
|
441
|
+
co = self._get_cobject()
|
|
442
|
+
isoFFI.clib.sortEnvelopeByProb(co)
|
|
443
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
444
|
+
self.mass_sorted = False
|
|
445
|
+
self.prob_sorted = True
|
|
446
|
+
|
|
447
|
+
def sort_by_mass(self):
|
|
448
|
+
if not self.mass_sorted:
|
|
449
|
+
co = self._get_cobject()
|
|
450
|
+
isoFFI.clib.sortEnvelopeByMass(co)
|
|
451
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
452
|
+
self.mass_sorted = True
|
|
453
|
+
self.prob_sorted = False
|
|
454
|
+
|
|
455
|
+
def _recalculate_everything(self):
|
|
456
|
+
self._total_prob = float('nan')
|
|
457
|
+
self.mass_sorted = False
|
|
458
|
+
self.prob_sorted = False
|
|
459
|
+
|
|
460
|
+
def empiric_average_mass(self):
|
|
461
|
+
co = self._get_cobject()
|
|
462
|
+
ret = isoFFI.clib.empiricAverageMass(co)
|
|
463
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
464
|
+
return ret
|
|
465
|
+
|
|
466
|
+
def empiric_variance(self):
|
|
467
|
+
co = self._get_cobject()
|
|
468
|
+
ret = isoFFI.clib.empiricVariance(co)
|
|
469
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
470
|
+
return ret
|
|
471
|
+
|
|
472
|
+
def empiric_stddev(self):
|
|
473
|
+
co = self._get_cobject()
|
|
474
|
+
ret = isoFFI.clib.empiricStddev(co)
|
|
475
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
476
|
+
return ret
|
|
477
|
+
|
|
478
|
+
def wassersteinDistance(self, other):
|
|
479
|
+
x = self._get_cobject()
|
|
480
|
+
y = other._get_cobject()
|
|
481
|
+
ret = isoFFI.clib.wassersteinDistance(x, y)
|
|
482
|
+
isoFFI.clib.deleteFixedEnvelope(x, True)
|
|
483
|
+
isoFFI.clib.deleteFixedEnvelope(y, True)
|
|
484
|
+
self.mass_sorted = True
|
|
485
|
+
self.prob_sorted = False
|
|
486
|
+
other.mass_sorted = True
|
|
487
|
+
other.prob_sorted = False
|
|
488
|
+
if math.isnan(ret):
|
|
489
|
+
raise ValueError("Both spectra must be normalized before Wasserstein distance can be computed.")
|
|
490
|
+
return ret
|
|
491
|
+
|
|
492
|
+
def orientedWassersteinDistance(self, other):
|
|
493
|
+
x = self._get_cobject()
|
|
494
|
+
y = other._get_cobject()
|
|
495
|
+
ret = isoFFI.clib.orientedWassersteinDistance(x, y)
|
|
496
|
+
isoFFI.clib.deleteFixedEnvelope(x, True)
|
|
497
|
+
isoFFI.clib.deleteFixedEnvelope(y, True)
|
|
498
|
+
self.mass_sorted = True
|
|
499
|
+
self.prob_sorted = False
|
|
500
|
+
other.mass_sorted = True
|
|
501
|
+
other.prob_sorted = False
|
|
502
|
+
if math.isnan(ret):
|
|
503
|
+
raise ValueError("Both spectra must be normalized before Wasserstein distance can be computed.")
|
|
504
|
+
return ret
|
|
505
|
+
|
|
506
|
+
def abyssalWassersteinDistance(self, other, abyss_depth, other_scale = 1.0):
|
|
507
|
+
x = self._get_cobject()
|
|
508
|
+
y = other._get_cobject()
|
|
509
|
+
ret = isoFFI.clib.abyssalWassersteinDistance(x, y, abyss_depth, other_scale)
|
|
510
|
+
isoFFI.clib.deleteFixedEnvelope(x, True)
|
|
511
|
+
isoFFI.clib.deleteFixedEnvelope(y, True)
|
|
512
|
+
self.mass_sorted = True
|
|
513
|
+
self.prob_sorted = False
|
|
514
|
+
other.mass_sorted = True
|
|
515
|
+
other.prob_sorted = False
|
|
516
|
+
return ret
|
|
517
|
+
|
|
518
|
+
def abyssalWassersteinDistanceGrad(self, others, scales, grad, abyss_depth_exp, abyss_depth_the):
|
|
519
|
+
assert len(others) + 1 == len(scales) == len(grad)
|
|
520
|
+
cobjs = [self._get_cobject()]
|
|
521
|
+
cobjs.extend(other._get_cobject() for other in others)
|
|
522
|
+
ret = isoFFI.clib.abyssalWassersteinDistanceGrad(cobjs, scales, grad, len(others), abyss_depth_exp, abyss_depth_the)
|
|
523
|
+
for cobj in cobjs:
|
|
524
|
+
isoFFI.clib.deleteFixedEnvelope(cobj, True)
|
|
525
|
+
self.mass_sorted = True
|
|
526
|
+
self.prob_sorted = False
|
|
527
|
+
for other in others:
|
|
528
|
+
other.mass_sorted = True
|
|
529
|
+
other.prob_sorted = False
|
|
530
|
+
return ret
|
|
531
|
+
|
|
532
|
+
def wassersteinMatch(self, other, flow_dist, other_scale = 1.0):
|
|
533
|
+
x = self._get_cobject()
|
|
534
|
+
y = other._get_cobject()
|
|
535
|
+
ret = isoFFI.clib.wassersteinMatch(x, y, flow_dist, other_scale)
|
|
536
|
+
isoFFI.clib.deleteFixedEnvelope(x, True)
|
|
537
|
+
isoFFI.clib.deleteFixedEnvelope(y, True)
|
|
538
|
+
self.mass_sorted = True
|
|
539
|
+
self.prob_sorted = False
|
|
540
|
+
other.mass_sorted = True
|
|
541
|
+
other.prob_sorted = False
|
|
542
|
+
return (ret.res1, ret.res2, ret.flow)
|
|
543
|
+
|
|
544
|
+
def binned(self, width = 1.0, middle = 0.0):
|
|
545
|
+
co = self._get_cobject()
|
|
546
|
+
cbo = isoFFI.clib.binnedEnvelope(co, width, middle)
|
|
547
|
+
isoFFI.clib.deleteFixedEnvelope(co, True)
|
|
548
|
+
ret = IsoDistribution(cobject = cbo)
|
|
549
|
+
isoFFI.clib.deleteFixedEnvelope(cbo, False)
|
|
550
|
+
self.mass_sorted = True
|
|
551
|
+
self.prob_sorted = False
|
|
552
|
+
ret.mass_sorted = True
|
|
553
|
+
ret.prob_sorted = False
|
|
554
|
+
return ret
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
@staticmethod
|
|
558
|
+
def LinearCombination(envelopes, intensities):
|
|
559
|
+
envelope_objs = [x._get_cobject() for x in envelopes]
|
|
560
|
+
if type(intensities) != list:
|
|
561
|
+
intensities = list(intensities)
|
|
562
|
+
cobject = isoFFI.clib.linearCombination(envelope_objs, intensities, len(envelope_objs))
|
|
563
|
+
for x in envelope_objs:
|
|
564
|
+
isoFFI.clib.deleteFixedEnvelope(x, True)
|
|
565
|
+
ret = IsoDistribution(cobject = cobject)
|
|
566
|
+
isoFFI.clib.deleteFixedEnvelope(cobject, False)
|
|
567
|
+
return ret
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def plot(self, plot_type = "bars", show = True, **matplotlib_args):
|
|
571
|
+
"""Plot the isotopic distribution.
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
**matplotlib_args: arguments for matplotlib plot.
|
|
575
|
+
"""
|
|
576
|
+
try:
|
|
577
|
+
from matplotlib import pyplot as plt
|
|
578
|
+
except ImportError as e:
|
|
579
|
+
raise ImportError(str(e) + "\nPlotting spectra requires matplotlib to be installed.")
|
|
580
|
+
if plot_type == "bars":
|
|
581
|
+
if "linewidth" not in matplotlib_args:
|
|
582
|
+
matplotlib_args['linewidth'] = 1.0
|
|
583
|
+
plt.vlines(list(self.masses), [0], list(self.probs), **matplotlib_args)
|
|
584
|
+
elif plot_type == "profile":
|
|
585
|
+
self.sort_by_mass()
|
|
586
|
+
plt.plot(list(self.masses), list(self.probs), **matplotlib_args)
|
|
587
|
+
plt.xlabel("Mass (Da)")
|
|
588
|
+
plt.ylabel("Intensity (relative)")
|
|
589
|
+
if show:
|
|
590
|
+
plt.show()
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def IsoThreshold(threshold,
|
|
596
|
+
formula="",
|
|
597
|
+
absolute=False,
|
|
598
|
+
get_confs=False,
|
|
599
|
+
**kwargs):
|
|
600
|
+
"""Initialize the IsoDistribution isotopic distribution by threshold.
|
|
601
|
+
|
|
602
|
+
Args:
|
|
603
|
+
threshold (float): value of the absolute or relative threshold.
|
|
604
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
605
|
+
absolute (boolean): should we report peaks with probabilities above an absolute probability threshold, or above a relative threshold amounting to a given proportion of the most probable peak?
|
|
606
|
+
get_confs (boolean): should we report counts of isotopologues?
|
|
607
|
+
**kwds: named arguments to IsoSpectrum.
|
|
608
|
+
"""
|
|
609
|
+
iso = Iso(formula = formula, get_confs = get_confs, **kwargs)
|
|
610
|
+
tabulator = isoFFI.clib.setupThresholdFixedEnvelope(iso.iso, threshold, absolute, get_confs)
|
|
611
|
+
ido = IsoDistribution(cobject = tabulator, get_confs = get_confs, iso = iso)
|
|
612
|
+
isoFFI.clib.deleteFixedEnvelope(tabulator, False)
|
|
613
|
+
return ido
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def IsoTotalProb(prob_to_cover,
|
|
617
|
+
formula="",
|
|
618
|
+
get_minimal_pset=True,
|
|
619
|
+
get_confs=False,
|
|
620
|
+
**kwargs):
|
|
621
|
+
"""Initialize the IsoDistribution isotopic distribution by total probability.
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
prob_to_cover (float): minimal total probability of the reported peaks.
|
|
625
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
626
|
+
get_minimal_pset (boolean): should we trim the last calculated layer of isotopologues so that the reported result is as small as possible?
|
|
627
|
+
get_confs (boolean): should we report the counts of isotopologues?
|
|
628
|
+
**kwargs: named arguments to the superclass.
|
|
629
|
+
"""
|
|
630
|
+
iso = Iso(formula=formula, get_confs=get_confs, **kwargs)
|
|
631
|
+
tabulator = isoFFI.clib.setupTotalProbFixedEnvelope(iso.iso, prob_to_cover, get_minimal_pset, get_confs)
|
|
632
|
+
ido = IsoDistribution(cobject = tabulator, get_confs = get_confs, iso = iso)
|
|
633
|
+
isoFFI.clib.deleteFixedEnvelope(tabulator, False)
|
|
634
|
+
return ido
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def IsoStochastic(no_molecules,
|
|
638
|
+
formula="",
|
|
639
|
+
precision=0.9999,
|
|
640
|
+
beta_bias=5.0,
|
|
641
|
+
get_confs=False,
|
|
642
|
+
**kwargs):
|
|
643
|
+
"""Initialize the IsoDistribution isotopic distribution by total probability.
|
|
644
|
+
|
|
645
|
+
Args:
|
|
646
|
+
no_molecules (uint): ionic current in instrument
|
|
647
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
648
|
+
precision (float): passed to IsoTotalProbGenerator. Between 0.0 and 1.0.
|
|
649
|
+
beta_bias (float, nonnegative): fiddling with this parameter does not change the result, but might make computations slightly faster (or likely, much, much slower is you screw it up...)
|
|
650
|
+
get_confs (boolean): should we report the counts of isotopologues?
|
|
651
|
+
**kwargs: named arguments to the superclass.
|
|
652
|
+
"""
|
|
653
|
+
iso = Iso(formula=formula, get_confs=get_confs, **kwargs)
|
|
654
|
+
tabulator = isoFFI.clib.setupStochasticFixedEnvelope(iso.iso, no_molecules, precision, beta_bias, get_confs)
|
|
655
|
+
ido = IsoDistribution(cobject = tabulator, get_confs = get_confs, iso = iso)
|
|
656
|
+
isoFFI.clib.deleteFixedEnvelope(tabulator, False)
|
|
657
|
+
return ido
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def IsoBinned(bin_width,
|
|
661
|
+
formula="",
|
|
662
|
+
target_total_prob=0.9999,
|
|
663
|
+
bin_middle=0.0,
|
|
664
|
+
**kwargs):
|
|
665
|
+
"""Initialize the IsoDistribution isotopic distribution by total probability.
|
|
666
|
+
|
|
667
|
+
Args:
|
|
668
|
+
no_molecules (uint): ionic current in instrument
|
|
669
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
670
|
+
precision (float): passed to IsoTotalProbGenerator. Between 0.0 and 1.0.
|
|
671
|
+
beta_bias (float, nonnegative): fiddling with this parameter does not change the result, but might make computations slightly faster (or likely, much, much slower is you screw it up...)
|
|
672
|
+
get_confs (boolean): should we report the counts of isotopologues?
|
|
673
|
+
**kwargs: named arguments to the superclass.
|
|
674
|
+
"""
|
|
675
|
+
iso = Iso(formula=formula, get_confs=False, **kwargs)
|
|
676
|
+
tabulator = isoFFI.clib.setupBinnedFixedEnvelope(iso.iso, target_total_prob, bin_width, bin_middle)
|
|
677
|
+
ido = IsoDistribution(cobject = tabulator, get_confs = False, iso = iso)
|
|
678
|
+
isoFFI.clib.deleteFixedEnvelope(tabulator, False)
|
|
679
|
+
return ido
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
class IsoGenerator(Iso):
|
|
683
|
+
"""Virtual class alowing memory-efficient iteration over the isotopic distribution.
|
|
684
|
+
|
|
685
|
+
This iterator will stop only after enumerating all isotopologues.
|
|
686
|
+
"""
|
|
687
|
+
def __init__(self, formula="", get_confs=False, **kwargs):
|
|
688
|
+
"""Initialize the IsoGenerator.
|
|
689
|
+
|
|
690
|
+
Args:
|
|
691
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
692
|
+
get_confs (boolean): should we report the counts of isotopologues?
|
|
693
|
+
**kwargs: named arguments to the superclass.
|
|
694
|
+
"""
|
|
695
|
+
self.cgen = None
|
|
696
|
+
super(IsoGenerator, self).__init__(formula=formula, get_confs=get_confs, **kwargs)
|
|
697
|
+
self.conf_space = isoFFI.ffi.new("int[" + str(sum(self.isotopeNumbers)) + "]")
|
|
698
|
+
self.firstuse = True
|
|
699
|
+
|
|
700
|
+
def __iter__(self):
|
|
701
|
+
if not self.firstuse:
|
|
702
|
+
raise NotImplementedError("Multiple iterations through the same IsoGenerator object are not supported. Either create a new (identical) generator for a second loop-through, or use one of the non-generator classes, which do support being re-used.")
|
|
703
|
+
self.firstuse = False
|
|
704
|
+
cgen = self.cgen
|
|
705
|
+
if self.get_confs:
|
|
706
|
+
while self.advancer(cgen):
|
|
707
|
+
self.conf_getter(cgen, self.conf_space)
|
|
708
|
+
yield (self.mass_getter(cgen), self.xprob_getter(cgen), self.parse_conf(self.conf_space))
|
|
709
|
+
else:
|
|
710
|
+
while self.advancer(cgen):
|
|
711
|
+
yield (self.mass_getter(cgen), self.xprob_getter(cgen))
|
|
712
|
+
|
|
713
|
+
def __del__(self):
|
|
714
|
+
super(IsoGenerator, self).__del__()
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
class IsoThresholdGenerator(IsoGenerator):
|
|
718
|
+
"""Class alowing memory-efficient iteration over the isotopic distribution up till some probability threshold.
|
|
719
|
+
|
|
720
|
+
This iterator will stop only after reaching a probability threshold.
|
|
721
|
+
"""
|
|
722
|
+
def __init__(self, threshold, formula="", absolute=False, get_confs=False, reorder_marginals = True, **kwargs):
|
|
723
|
+
"""Initialize IsoThresholdGenerator.
|
|
724
|
+
|
|
725
|
+
Args:
|
|
726
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
727
|
+
absolute (boolean): should we report peaks with probabilities above an absolute probability threshold, or above a relative threshold amounting to a given proportion of the most probable peak?
|
|
728
|
+
get_confs (boolean): should we report the counts of isotopologues?
|
|
729
|
+
**kwargs: named arguments to the superclass.
|
|
730
|
+
"""
|
|
731
|
+
super(IsoThresholdGenerator, self).__init__(formula=formula, get_confs=get_confs, **kwargs)
|
|
732
|
+
self.threshold = threshold
|
|
733
|
+
self.absolute = absolute
|
|
734
|
+
self.cgen = self.ffi.setupIsoThresholdGenerator(self.iso,
|
|
735
|
+
threshold,
|
|
736
|
+
absolute,
|
|
737
|
+
1000,
|
|
738
|
+
1000,
|
|
739
|
+
reorder_marginals)
|
|
740
|
+
self.advancer = self.ffi.advanceToNextConfigurationIsoThresholdGenerator
|
|
741
|
+
self.xprob_getter = self.ffi.probIsoThresholdGenerator
|
|
742
|
+
self.mass_getter = self.ffi.massIsoThresholdGenerator
|
|
743
|
+
self.conf_getter = self.ffi.get_conf_signatureIsoThresholdGenerator
|
|
744
|
+
|
|
745
|
+
def __del__(self):
|
|
746
|
+
"""Destructor."""
|
|
747
|
+
try:
|
|
748
|
+
if self.cgen is not None:
|
|
749
|
+
self.ffi.deleteIsoThresholdGenerator(self.cgen)
|
|
750
|
+
self.cgen = None
|
|
751
|
+
except AttributeError:
|
|
752
|
+
pass
|
|
753
|
+
super(IsoThresholdGenerator, self).__del__()
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
class IsoLayeredGenerator(IsoGenerator):
|
|
757
|
+
"""Class alowing memory-efficient iteration over the isotopic distribution up till some joint probability of the reported peaks."""
|
|
758
|
+
def __init__(self, formula="", get_confs=False, reorder_marginals = True, t_prob_hint = 0.99, **kwargs):
|
|
759
|
+
"""Initialize IsoThresholdGenerator.
|
|
760
|
+
|
|
761
|
+
Args:
|
|
762
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
763
|
+
absolute (boolean): should we report peaks with probabilities above an absolute probability threshold, or above a relative threshold amounting to a given proportion of the most probable peak?
|
|
764
|
+
get_confs (boolean): should we report the counts of isotopologues?
|
|
765
|
+
**kwargs: named arguments to the superclass.
|
|
766
|
+
"""
|
|
767
|
+
super(IsoLayeredGenerator, self).__init__(formula=formula, get_confs=get_confs, **kwargs)
|
|
768
|
+
self.cgen = self.ffi.setupIsoLayeredGenerator(self.iso, 1000, 1000, reorder_marginals, t_prob_hint)
|
|
769
|
+
self.advancer = self.ffi.advanceToNextConfigurationIsoLayeredGenerator
|
|
770
|
+
self.xprob_getter = self.ffi.probIsoLayeredGenerator
|
|
771
|
+
self.mass_getter = self.ffi.massIsoLayeredGenerator
|
|
772
|
+
self.conf_getter = self.ffi.get_conf_signatureIsoLayeredGenerator
|
|
773
|
+
|
|
774
|
+
def __del__(self):
|
|
775
|
+
try:
|
|
776
|
+
if self.cgen is not None:
|
|
777
|
+
self.ffi.deleteIsoLayeredGenerator(self.cgen)
|
|
778
|
+
self.cgen = None
|
|
779
|
+
except AttributeError:
|
|
780
|
+
pass
|
|
781
|
+
super(IsoLayeredGenerator, self).__del__()
|
|
782
|
+
|
|
783
|
+
class IsoOrderedGenerator(IsoGenerator):
|
|
784
|
+
"""Class representing an isotopic distribution with peaks ordered by descending probability.
|
|
785
|
+
|
|
786
|
+
This generator return probilities ordered with descending probability.
|
|
787
|
+
It it not optimal to do so, but it might be useful.
|
|
788
|
+
|
|
789
|
+
WARNING! This algorithm work in O(N*log(N)) vs O(N) of the threshold and layered algorithms.
|
|
790
|
+
Also, the order of descending probability will most likely not reflect the order of ascending masses.
|
|
791
|
+
"""
|
|
792
|
+
def __init__(self, formula="", get_confs=False, **kwargs):
|
|
793
|
+
"""Initialize IsoOrderedGenerator.
|
|
794
|
+
|
|
795
|
+
Args:
|
|
796
|
+
formula (str): a chemical formula, e.g. "C2H6O1" or "C2H6O".
|
|
797
|
+
get_confs (boolean): should we report the counts of isotopologues?
|
|
798
|
+
**kwargs: named arguments to the superclass.
|
|
799
|
+
"""
|
|
800
|
+
super(IsoOrderedGenerator, self).__init__(formula=formula, get_confs=get_confs, **kwargs)
|
|
801
|
+
self.cgen = self.ffi.setupIsoOrderedGenerator(self.iso, 1000, 1000)
|
|
802
|
+
self.advancer = self.ffi.advanceToNextConfigurationIsoOrderedGenerator
|
|
803
|
+
self.xprob_getter = self.ffi.probIsoOrderedGenerator
|
|
804
|
+
self.mass_getter = self.ffi.massIsoOrderedGenerator
|
|
805
|
+
self.conf_getter = self.ffi.get_conf_signatureIsoOrderedGenerator
|
|
806
|
+
|
|
807
|
+
def __del__(self):
|
|
808
|
+
try:
|
|
809
|
+
if self.cgen is not None:
|
|
810
|
+
self.ffi.deleteIsoLayeredGenerator(self.cgen)
|
|
811
|
+
self.cgen = None
|
|
812
|
+
except AttributeError:
|
|
813
|
+
pass
|
|
814
|
+
super(IsoOrderedGenerator, self).__del__()
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
class IsoStochasticGenerator(IsoGenerator):
|
|
818
|
+
def __init__(self, no_molecules, formula="", precision=0.9999, beta_bias = 1.0, get_confs=False, **kwargs):
|
|
819
|
+
super(IsoStochasticGenerator, self).__init__(formula=formula, get_confs=get_confs, **kwargs)
|
|
820
|
+
self.threshold = precision
|
|
821
|
+
self.no_molecules = no_molecules
|
|
822
|
+
self.cgen = self.ffi.setupIsoStochasticGenerator(self.iso,
|
|
823
|
+
no_molecules,
|
|
824
|
+
precision,
|
|
825
|
+
beta_bias)
|
|
826
|
+
self.advancer = self.ffi.advanceToNextConfigurationIsoStochasticGenerator
|
|
827
|
+
self.xprob_getter = self.ffi.probIsoStochasticGenerator
|
|
828
|
+
self.mass_getter = self.ffi.massIsoStochasticGenerator
|
|
829
|
+
self.conf_getter = self.ffi.get_conf_signatureIsoStochasticGenerator
|
|
830
|
+
|
|
831
|
+
def __del__(self):
|
|
832
|
+
"""Destructor."""
|
|
833
|
+
try:
|
|
834
|
+
if self.cgen is not None:
|
|
835
|
+
self.ffi.deleteIsoStochasticGenerator(self.cgen)
|
|
836
|
+
self.cgen = None
|
|
837
|
+
except AttributeError:
|
|
838
|
+
pass
|
|
839
|
+
super(IsoStochasticGenerator, self).__del__()
|
|
840
|
+
|