isodec 2.0.0b2__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.
- isodec/__init__.py +8 -0
- isodec/__main__.py +45 -0
- isodec/_version.py +3 -0
- isodec/altdecon.py +127 -0
- isodec/bin/isodeclib.dll +0 -0
- isodec/bin/isogen.dll +0 -0
- isodec/bin/libmmd.dll +0 -0
- isodec/bin/svml_dispmd.dll +0 -0
- isodec/bin_to_header.py +56 -0
- isodec/c_interface.py +434 -0
- isodec/compare_tools.py +685 -0
- isodec/config.py +61 -0
- isodec/datatools.py +454 -0
- isodec/encoding.py +302 -0
- isodec/encoding_profile.py +257 -0
- isodec/engine.py +952 -0
- isodec/fwhm.py +121 -0
- isodec/io.py +79 -0
- isodec/isotope.py +83 -0
- isodec/match.py +1192 -0
- isodec/modelparams/phase_model_1.bin +0 -0
- isodec/modelparams/phase_model_1.pth +0 -0
- isodec/modelparams/phase_model_4.bin +0 -0
- isodec/modelparams/phase_model_4.pth +0 -0
- isodec/modelparams/phase_model_8.bin +0 -0
- isodec/modelparams/phase_model_8.pth +0 -0
- isodec/modelparams/phase_model_b_1_0.bin +0 -0
- isodec/modelparams/phase_model_b_1_0.pth +0 -0
- isodec/modelparams/phase_model_b_4_0.bin +0 -0
- isodec/modelparams/phase_model_b_4_0.pth +0 -0
- isodec/modelparams/phase_model_b_4_1.bin +0 -0
- isodec/modelparams/phase_model_b_4_1.pth +0 -0
- isodec/modelparams/phase_model_b_8_0.bin +0 -0
- isodec/modelparams/phase_model_b_8_0.pth +0 -0
- isodec/modelparams/phase_model_b_8_1.bin +0 -0
- isodec/modelparams/phase_model_b_8_1.pth +0 -0
- isodec/modelparams/phase_model_c_1_0.bin +0 -0
- isodec/modelparams/phase_model_c_1_0.pth +0 -0
- isodec/modelparams/phase_model_c_4_0.bin +0 -0
- isodec/modelparams/phase_model_c_4_0.pth +0 -0
- isodec/modelparams/phase_model_c_4_1.bin +0 -0
- isodec/modelparams/phase_model_c_4_1.pth +0 -0
- isodec/modelparams/phase_model_c_8_0.bin +0 -0
- isodec/modelparams/phase_model_c_8_0.pth +0 -0
- isodec/modelparams/phase_model_c_8_1.bin +0 -0
- isodec/modelparams/phase_model_c_8_1.pth +0 -0
- isodec/modelparams/phase_model_p_1_0.bin +0 -0
- isodec/modelparams/phase_model_p_1_0.pth +0 -0
- isodec/modelparams/phase_model_p_4_0.bin +0 -0
- isodec/modelparams/phase_model_p_4_0.pth +0 -0
- isodec/modelparams/phase_model_p_4_1.bin +0 -0
- isodec/modelparams/phase_model_p_4_1.pth +0 -0
- isodec/modelparams/phase_model_p_8_0.bin +0 -0
- isodec/modelparams/phase_model_p_8_0.pth +0 -0
- isodec/modelparams/phase_model_p_8_1.bin +0 -0
- isodec/modelparams/phase_model_p_8_1.pth +0 -0
- isodec/models.py +553 -0
- isodec/msalign_export.py +211 -0
- isodec/plots.py +254 -0
- isodec/runtime.py +277 -0
- isodec/tools.py +112 -0
- isodec/train.py +37 -0
- isodec/train_profile.py +102 -0
- isodec/trainingdata.py +211 -0
- isodec/trainingdata_profile.py +192 -0
- isodec-2.0.0b2.dist-info/METADATA +121 -0
- isodec-2.0.0b2.dist-info/RECORD +74 -0
- isodec-2.0.0b2.dist-info/WHEEL +5 -0
- isodec-2.0.0b2.dist-info/entry_points.txt +3 -0
- isodec-2.0.0b2.dist-info/licenses/LICENSE +28 -0
- isodec-2.0.0b2.dist-info/licenses/THIRD_PARTY_NOTICES.md +12 -0
- isodec-2.0.0b2.dist-info/licenses/extern/IsoGen/LICENSE +11 -0
- isodec-2.0.0b2.dist-info/licenses/extern/IsoGen/src/fftw/COPYING +340 -0
- isodec-2.0.0b2.dist-info/licenses/extern/IsoGen/src/fftw/COPYRIGHT +19 -0
isodec/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""IsoDec charge-state assignment and deconvolution."""
|
|
2
|
+
|
|
3
|
+
from ._version import __version__
|
|
4
|
+
from .config import IsoDecConfig
|
|
5
|
+
from .c_interface import IsoDecWrapper
|
|
6
|
+
from .runtime import IsoDecRuntime
|
|
7
|
+
|
|
8
|
+
__all__ = ["IsoDecConfig", "IsoDecRuntime", "IsoDecWrapper", "__version__"]
|
isodec/__main__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Command-line interface for IsoDec."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
if not __package__:
|
|
8
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
9
|
+
__package__ = "isodec"
|
|
10
|
+
|
|
11
|
+
from .runtime import IsoDecRuntime
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_parser():
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
prog="isodec",
|
|
17
|
+
description="Assign charge states and deconvolve isotopically resolved mass spectra.",
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument("spectrum", type=Path, help="Input spectrum (TXT, DAT, CSV, NPZ, or a UniDec-supported format)")
|
|
20
|
+
parser.add_argument("-o", "--output", type=Path, help="Output TSV filename")
|
|
21
|
+
parser.add_argument("--centroided", action="store_true", help="Treat the input as centroid data")
|
|
22
|
+
parser.add_argument("--type", choices=("PEPTIDE", "RNA"), default="PEPTIDE", help="Analyte isotope model")
|
|
23
|
+
parser.add_argument("--quiet", action="store_true", help="Suppress progress output")
|
|
24
|
+
return parser
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main(argv=None):
|
|
28
|
+
args = build_parser().parse_args(argv)
|
|
29
|
+
if not args.spectrum.is_file():
|
|
30
|
+
raise SystemExit(f"Spectrum does not exist: {args.spectrum}")
|
|
31
|
+
|
|
32
|
+
engine = IsoDecRuntime(verbose=not args.quiet)
|
|
33
|
+
engine.analyte_type = args.type
|
|
34
|
+
engine.process_file(
|
|
35
|
+
str(args.spectrum),
|
|
36
|
+
assume_centroided=args.centroided,
|
|
37
|
+
verbose=not args.quiet,
|
|
38
|
+
)
|
|
39
|
+
output = args.output or args.spectrum.with_name(f"{args.spectrum.stem}_isodec.tsv")
|
|
40
|
+
engine.pks.export_tsv(output, report_multiple_monoisos=engine.config.report_multiple_monoisos)
|
|
41
|
+
return 0
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
raise SystemExit(main())
|
isodec/_version.py
ADDED
isodec/altdecon.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
if not __package__:
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
8
|
+
__package__ = "isodec"
|
|
9
|
+
|
|
10
|
+
from . import tools as ud
|
|
11
|
+
|
|
12
|
+
example = np.array(
|
|
13
|
+
[
|
|
14
|
+
[5.66785531e02, 1.47770838e06],
|
|
15
|
+
[5.67057354e02, 1.54980838e06],
|
|
16
|
+
[5.67507468e02, 5.21600520e07],
|
|
17
|
+
[5.67708173e02, 8.35557760e07],
|
|
18
|
+
[5.67908401e02, 7.28264240e07],
|
|
19
|
+
[5.68060254e02, 1.87337225e06],
|
|
20
|
+
[5.68108674e02, 4.35435520e07],
|
|
21
|
+
[5.68239256e02, 3.88155375e06],
|
|
22
|
+
[5.68309390e02, 2.05468060e07],
|
|
23
|
+
[5.68509951e02, 7.18109250e06],
|
|
24
|
+
[5.68707871e02, 2.30373500e06],
|
|
25
|
+
[5.69150563e02, 1.57598062e06],
|
|
26
|
+
[5.69243121e02, 1.96390440e07],
|
|
27
|
+
[5.69334393e02, 6.82677120e07],
|
|
28
|
+
[5.69425337e02, 1.22867432e08],
|
|
29
|
+
[5.69516492e02, 1.45702336e08],
|
|
30
|
+
[5.69607541e02, 1.20801936e08],
|
|
31
|
+
[5.69698595e02, 1.06786072e08],
|
|
32
|
+
[5.69789906e02, 6.56232960e07],
|
|
33
|
+
[5.69881208e02, 3.41013880e07],
|
|
34
|
+
[5.69972168e02, 1.70930360e07],
|
|
35
|
+
[5.70063432e02, 9.17621100e06],
|
|
36
|
+
[5.70699369e02, 1.96462650e06],
|
|
37
|
+
]
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
import numpy as np
|
|
41
|
+
from scipy import signal
|
|
42
|
+
|
|
43
|
+
MAX_CHARGE = 50
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def gen_thrash_arrays(centroids, startpad=10):
|
|
47
|
+
max_mz = centroids[-1, 0]
|
|
48
|
+
min_mz = centroids[0, 0]
|
|
49
|
+
|
|
50
|
+
additional = 4 - (max_mz - min_mz)
|
|
51
|
+
if additional < 0:
|
|
52
|
+
additional = 0
|
|
53
|
+
else:
|
|
54
|
+
max_mz += additional / 2
|
|
55
|
+
min_mz -= additional / 2
|
|
56
|
+
|
|
57
|
+
num_l = int((max_mz - min_mz) * MAX_CHARGE * 8)
|
|
58
|
+
|
|
59
|
+
intx = np.linspace(min_mz, max_mz, num_l)
|
|
60
|
+
if len(intx) <= 1:
|
|
61
|
+
return None, None, None, None, None, None
|
|
62
|
+
|
|
63
|
+
linear_data = ud.lintegrate(centroids, intx, fastmode=True)
|
|
64
|
+
if len(linear_data) <= 1:
|
|
65
|
+
return None, None, None, None, None, None
|
|
66
|
+
|
|
67
|
+
corry = signal.fftconvolve(linear_data[:, 1], linear_data[:, 1][::-1], mode='same')
|
|
68
|
+
maxposition = np.argmax(corry)
|
|
69
|
+
ac = corry[maxposition + startpad:]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
acx = intx - intx[0]
|
|
73
|
+
acx = 1 / acx[startpad:len(ac) + startpad]
|
|
74
|
+
|
|
75
|
+
fft = np.fft.fft(linear_data[:, 1])
|
|
76
|
+
fft = np.abs(fft)
|
|
77
|
+
fft = fft[startpad:len(ac) + startpad]
|
|
78
|
+
|
|
79
|
+
fftx = np.fft.fftfreq(len(linear_data[:, 1]), d=(linear_data[1, 0] - linear_data[0, 0]))
|
|
80
|
+
fftx = fftx[startpad:len(ac) + startpad]
|
|
81
|
+
b1 = fftx < MAX_CHARGE
|
|
82
|
+
fft = fft[b1]
|
|
83
|
+
fftx = fftx[b1]
|
|
84
|
+
|
|
85
|
+
fft /= np.max(fft)
|
|
86
|
+
ac2 = ud.lintegrate(np.transpose([acx, ac]), fftx)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
acx, ac = ac2[:, 0], ac2[:, 1]
|
|
90
|
+
ac /= np.max(ac)
|
|
91
|
+
mul = ac * fft
|
|
92
|
+
return linear_data, ac, fft, mul, fftx, acx
|
|
93
|
+
|
|
94
|
+
def thrash_predict(centroids):
|
|
95
|
+
linear_data, ac, fft, mul, fftx, acx = gen_thrash_arrays(centroids)
|
|
96
|
+
if linear_data is None or len(mul) == 0:
|
|
97
|
+
return 0
|
|
98
|
+
maxpos = np.argmax(mul)
|
|
99
|
+
|
|
100
|
+
charge = acx[maxpos]
|
|
101
|
+
return round(charge)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
example = np.loadtxt("Z:\\Group Share\\JGP\\MockData\\mockdata_centroids.csv", delimiter=",")
|
|
106
|
+
example = ud.datachop(example, 326,327)
|
|
107
|
+
for i in range(len(example)):
|
|
108
|
+
plt.plot([example[i, 0], example[i, 0]], [0, example[i, 1]], color="black")
|
|
109
|
+
plt.show()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
raw_data = example
|
|
114
|
+
charge_state = thrash_predict(raw_data)
|
|
115
|
+
print(charge_state)
|
|
116
|
+
|
|
117
|
+
linear_data, ac, fft, mul, fftx, acx = gen_thrash_arrays(raw_data)
|
|
118
|
+
|
|
119
|
+
plt.subplot(121)
|
|
120
|
+
plt.plot(linear_data[:, 0], linear_data[:, 1])
|
|
121
|
+
plt.subplot(122)
|
|
122
|
+
plt.plot(acx, mul, label="Mul")
|
|
123
|
+
plt.plot(acx, ac-1, label="AC")
|
|
124
|
+
plt.plot(fftx, fft-2, label="FFT")
|
|
125
|
+
plt.legend()
|
|
126
|
+
plt.show()
|
|
127
|
+
|
isodec/bin/isodeclib.dll
ADDED
|
Binary file
|
isodec/bin/isogen.dll
ADDED
|
Binary file
|
isodec/bin/libmmd.dll
ADDED
|
Binary file
|
|
Binary file
|
isodec/bin_to_header.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Convert a PyTorch model to a binary file and C header."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def pth_to_bin(pth_file, bin_file):
|
|
9
|
+
"""Flatten a PyTorch state dictionary into a raw float32 binary file."""
|
|
10
|
+
pth_file = Path(pth_file)
|
|
11
|
+
bin_file = Path(bin_file)
|
|
12
|
+
|
|
13
|
+
state_dict = torch.load(pth_file, map_location="cpu", weights_only=True)
|
|
14
|
+
weights = torch.cat([tensor.flatten() for tensor in state_dict.values()])
|
|
15
|
+
weights.detach().cpu().numpy().tofile(bin_file)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def bin_to_header(bin_file, header_file, varname="model_weights"):
|
|
19
|
+
"""Write the bytes from ``bin_file`` to a C array in ``header_file``."""
|
|
20
|
+
bin_file = Path(bin_file)
|
|
21
|
+
header_file = Path(header_file)
|
|
22
|
+
data = bin_file.read_bytes()
|
|
23
|
+
guard = f"{varname.upper()}_H"
|
|
24
|
+
|
|
25
|
+
with header_file.open("w", encoding="ascii", newline="\n") as outfile:
|
|
26
|
+
outfile.write(f"#ifndef {guard}\n")
|
|
27
|
+
outfile.write(f"#define {guard}\n\n")
|
|
28
|
+
outfile.write(f"// Generated from {bin_file.name}\n")
|
|
29
|
+
outfile.write(f"static const unsigned char {varname}[] = {{\n")
|
|
30
|
+
|
|
31
|
+
for start in range(0, len(data), 12):
|
|
32
|
+
chunk = data[start:start + 12]
|
|
33
|
+
values = ", ".join(f"0x{byte:02x}" for byte in chunk)
|
|
34
|
+
if start + 12 < len(data):
|
|
35
|
+
values += ","
|
|
36
|
+
outfile.write(f" {values}\n")
|
|
37
|
+
|
|
38
|
+
outfile.write("};\n")
|
|
39
|
+
outfile.write(f"static const unsigned int {varname}_len = {len(data)};\n\n")
|
|
40
|
+
outfile.write(f"#endif // {guard}\n")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
isodec_dir = Path(__file__).resolve().parent
|
|
45
|
+
|
|
46
|
+
model_dir = isodec_dir / "modelparams"
|
|
47
|
+
input_pth = model_dir / "phase_model_8.pth"
|
|
48
|
+
output_bin = model_dir / "phase_model_8.bin"
|
|
49
|
+
output_header = isodec_dir / "src_cmake" / "phase_model_8.h"
|
|
50
|
+
variable_name = "phase_model_8_bin"
|
|
51
|
+
|
|
52
|
+
pth_to_bin(input_pth, output_bin)
|
|
53
|
+
print("Wrote binary model:", output_bin)
|
|
54
|
+
|
|
55
|
+
bin_to_header(output_bin, output_header, variable_name)
|
|
56
|
+
print("Wrote C header:", output_header)
|
isodec/c_interface.py
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from importlib import metadata
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
if not __package__:
|
|
11
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
12
|
+
__package__ = "isodec"
|
|
13
|
+
|
|
14
|
+
from .match import MatchedPeak, MatchedCollection
|
|
15
|
+
from .config import IsoDecConfig
|
|
16
|
+
|
|
17
|
+
_system = platform.system()
|
|
18
|
+
_library_names = {
|
|
19
|
+
"Windows": "isodeclib.dll",
|
|
20
|
+
"Linux": "isodeclib.so",
|
|
21
|
+
"Darwin": "isodeclib.dylib",
|
|
22
|
+
}
|
|
23
|
+
try:
|
|
24
|
+
dllname = _library_names[_system]
|
|
25
|
+
except KeyError as error:
|
|
26
|
+
raise ImportError(f"IsoDec does not support {_system!r}") from error
|
|
27
|
+
|
|
28
|
+
_package_dir = Path(__file__).resolve().parent
|
|
29
|
+
_packaged_library_path = _package_dir / "bin" / dllname
|
|
30
|
+
_source_library_path = _package_dir / dllname
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _editable_library_path():
|
|
34
|
+
"""Locate scikit-build's native install during an editable install."""
|
|
35
|
+
try:
|
|
36
|
+
candidate = Path(
|
|
37
|
+
metadata.distribution("isodec").locate_file(
|
|
38
|
+
Path("isodec") / "bin" / dllname
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
except metadata.PackageNotFoundError:
|
|
42
|
+
return None
|
|
43
|
+
return candidate if candidate.is_file() else None
|
|
44
|
+
|
|
45
|
+
if _packaged_library_path.is_file():
|
|
46
|
+
default_dll_path = _packaged_library_path
|
|
47
|
+
elif (_package_dir.parent / "pyproject.toml").is_file() and _source_library_path.is_file():
|
|
48
|
+
default_dll_path = _source_library_path
|
|
49
|
+
elif _editable_library_path() is not None:
|
|
50
|
+
default_dll_path = _editable_library_path()
|
|
51
|
+
else:
|
|
52
|
+
default_dll_path = _packaged_library_path
|
|
53
|
+
|
|
54
|
+
example = np.array(
|
|
55
|
+
[
|
|
56
|
+
[5.66785531e02, 1.47770838e06],
|
|
57
|
+
[5.67057354e02, 1.54980838e06],
|
|
58
|
+
[5.67507468e02, 5.21600520e07],
|
|
59
|
+
[5.67708173e02, 8.35557760e07],
|
|
60
|
+
[5.67908401e02, 7.28264240e07],
|
|
61
|
+
[5.68060254e02, 1.87337225e06],
|
|
62
|
+
[5.68108674e02, 4.35435520e07],
|
|
63
|
+
[5.68239256e02, 3.88155375e06],
|
|
64
|
+
[5.68309390e02, 2.05468060e07],
|
|
65
|
+
[5.68509951e02, 7.18109250e06],
|
|
66
|
+
[5.68707871e02, 2.30373500e06],
|
|
67
|
+
[5.69150563e02, 1.57598062e06],
|
|
68
|
+
[5.69243121e02, 1.96390440e07],
|
|
69
|
+
[5.69334393e02, 6.82677120e07],
|
|
70
|
+
[5.69425337e02, 1.22867432e08],
|
|
71
|
+
[5.69516492e02, 1.45702336e08],
|
|
72
|
+
[5.69607541e02, 1.20801936e08],
|
|
73
|
+
[5.69698595e02, 1.06786072e08],
|
|
74
|
+
[5.69789906e02, 6.56232960e07],
|
|
75
|
+
[5.69881208e02, 3.41013880e07],
|
|
76
|
+
[5.69972168e02, 1.70930360e07],
|
|
77
|
+
[5.70063432e02, 9.17621100e06],
|
|
78
|
+
[5.70699369e02, 1.96462650e06],
|
|
79
|
+
]
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
isodist = ctypes.c_float * 64
|
|
83
|
+
matchedinds = ctypes.c_int * 32
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# print(isodist)
|
|
87
|
+
class MPStruct(ctypes.Structure):
|
|
88
|
+
_fields_ = [
|
|
89
|
+
("mz", ctypes.c_float),
|
|
90
|
+
("z", ctypes.c_int),
|
|
91
|
+
("monoiso", ctypes.c_float),
|
|
92
|
+
("peakmass", ctypes.c_float),
|
|
93
|
+
("avgmass", ctypes.c_float),
|
|
94
|
+
("area", ctypes.c_float),
|
|
95
|
+
("peakint", ctypes.c_float),
|
|
96
|
+
("matchedindsiso", ctypes.c_int * 64),
|
|
97
|
+
("matchedindsexp", ctypes.c_int * 64),
|
|
98
|
+
("isomz", isodist),
|
|
99
|
+
("isodist", isodist),
|
|
100
|
+
("isomass", isodist),
|
|
101
|
+
("monoisos", ctypes.c_float * 16),
|
|
102
|
+
("startindex", ctypes.c_int),
|
|
103
|
+
("endindex", ctypes.c_int),
|
|
104
|
+
("score", ctypes.c_float),
|
|
105
|
+
("realisolength", ctypes.c_int),
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class IDSettings(ctypes.Structure):
|
|
110
|
+
_fields_ = [
|
|
111
|
+
("phaseres", ctypes.c_int),
|
|
112
|
+
("verbose", ctypes.c_int),
|
|
113
|
+
("peakwindow", ctypes.c_int),
|
|
114
|
+
("peakthresh", ctypes.c_float),
|
|
115
|
+
("minpeaks", ctypes.c_int),
|
|
116
|
+
("css_thresh", ctypes.c_float),
|
|
117
|
+
("matchtol", ctypes.c_float),
|
|
118
|
+
("maxshift", ctypes.c_int),
|
|
119
|
+
("mzwindow", ctypes.c_float * 2),
|
|
120
|
+
("plusoneintwindow", ctypes.c_float * 2),
|
|
121
|
+
("knockdown_rounds", ctypes.c_int),
|
|
122
|
+
("min_score_diff", ctypes.c_float),
|
|
123
|
+
("minareacovered", ctypes.c_float),
|
|
124
|
+
("isolength", ctypes.c_int),
|
|
125
|
+
("mass_diff_c", ctypes.c_double),
|
|
126
|
+
("adductmass", ctypes.c_float),
|
|
127
|
+
("minusoneaszero", ctypes.c_int),
|
|
128
|
+
("isotopethreshold", ctypes.c_float),
|
|
129
|
+
("datathreshold", ctypes.c_float),
|
|
130
|
+
("zscore_threshold", ctypes.c_float),
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class IDConfig(ctypes.Structure):
|
|
135
|
+
_fields_ = [
|
|
136
|
+
("verbose", ctypes.c_int),
|
|
137
|
+
("pres", ctypes.c_int),
|
|
138
|
+
("maxz", ctypes.c_int),
|
|
139
|
+
("elen", ctypes.c_int),
|
|
140
|
+
("l1", ctypes.c_int),
|
|
141
|
+
("l2", ctypes.c_int),
|
|
142
|
+
("l3", ctypes.c_int),
|
|
143
|
+
("l4", ctypes.c_int),
|
|
144
|
+
("dlen", ctypes.c_int),
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def config_to_settings(config):
|
|
149
|
+
# print(config)
|
|
150
|
+
settings = IDSettings()
|
|
151
|
+
settings.phaseres = int(config.phaseres)
|
|
152
|
+
settings.verbose = int(config.verbose)
|
|
153
|
+
settings.peakwindow = int(config.peakwindow)
|
|
154
|
+
settings.peakthresh = float(config.peakthresh)
|
|
155
|
+
settings.minpeaks = int(config.minpeaks)
|
|
156
|
+
settings.css_thresh = float(config.css_thresh)
|
|
157
|
+
settings.matchtol = float(config.matchtol)
|
|
158
|
+
settings.maxshift = int(config.maxshift)
|
|
159
|
+
settings.mzwindow = (config.mzwindowlb, config.mzwindowub)
|
|
160
|
+
settings.plusoneintwindow = (config.plusoneintwindowlb, config.plusoneintwindowub)
|
|
161
|
+
settings.knockdown_rounds = int(config.knockdown_rounds)
|
|
162
|
+
settings.min_score_diff = config.min_score_diff
|
|
163
|
+
settings.minareacovered = config.minareacovered
|
|
164
|
+
settings.isolength = 64
|
|
165
|
+
settings.mass_diff_c = config.mass_diff_c
|
|
166
|
+
settings.adductmass = config.adductmass
|
|
167
|
+
settings.minusoneaszero = config.minusoneaszero
|
|
168
|
+
settings.isotopethreshold = config.isotopethreshold
|
|
169
|
+
settings.datathreshold = config.datathreshold
|
|
170
|
+
settings.zscore_threshold = config.zscore_threshold
|
|
171
|
+
return settings
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class IsoDecWrapper:
|
|
175
|
+
def __init__(self, dllpath=None):
|
|
176
|
+
if dllpath is None:
|
|
177
|
+
dllpath = default_dll_path
|
|
178
|
+
|
|
179
|
+
dllpath = Path(dllpath).resolve()
|
|
180
|
+
if not dllpath.is_file():
|
|
181
|
+
raise ImportError(
|
|
182
|
+
f"IsoDec's native library is missing: {dllpath}. Reinstall "
|
|
183
|
+
"isodec using a compatible wheel, or build from source "
|
|
184
|
+
"with CMake and a native compiler."
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
modelpath = _package_dir / "modelparams"
|
|
188
|
+
|
|
189
|
+
self.modeldir = modelpath
|
|
190
|
+
|
|
191
|
+
self._dll_directory_handle = None
|
|
192
|
+
if _system == "Windows":
|
|
193
|
+
self._dll_directory_handle = os.add_dll_directory(str(dllpath.parent))
|
|
194
|
+
try:
|
|
195
|
+
self.c_lib = ctypes.CDLL(str(dllpath))
|
|
196
|
+
except OSError as error:
|
|
197
|
+
raise ImportError(f"Unable to load IsoDec's native library {dllpath}: {error}") from error
|
|
198
|
+
|
|
199
|
+
self.c_lib.encode.argtypes = [
|
|
200
|
+
ctypes.POINTER(ctypes.c_double),
|
|
201
|
+
ctypes.POINTER(ctypes.c_float),
|
|
202
|
+
ctypes.c_int,
|
|
203
|
+
ctypes.POINTER(ctypes.c_float),
|
|
204
|
+
IDConfig,
|
|
205
|
+
IDSettings,
|
|
206
|
+
]
|
|
207
|
+
self.c_lib.encode.restype = ctypes.c_int
|
|
208
|
+
|
|
209
|
+
self.c_lib.predict_charge.argtypes = [
|
|
210
|
+
ctypes.POINTER(ctypes.c_double),
|
|
211
|
+
ctypes.POINTER(ctypes.c_float),
|
|
212
|
+
ctypes.c_int,
|
|
213
|
+
ctypes.c_char_p, # Model path
|
|
214
|
+
]
|
|
215
|
+
self.c_lib.predict_charge.restype = ctypes.c_int
|
|
216
|
+
|
|
217
|
+
self.c_lib.process_spectrum.argtypes = [
|
|
218
|
+
ctypes.POINTER(ctypes.c_double),
|
|
219
|
+
ctypes.POINTER(ctypes.c_float),
|
|
220
|
+
ctypes.c_int,
|
|
221
|
+
ctypes.c_char_p,
|
|
222
|
+
ctypes.POINTER(MPStruct),
|
|
223
|
+
IDSettings,
|
|
224
|
+
ctypes.c_char_p,
|
|
225
|
+
]
|
|
226
|
+
self.c_lib.process_spectrum.restype = ctypes.c_int
|
|
227
|
+
|
|
228
|
+
self.c_lib.DefaultSettings.argtypes = []
|
|
229
|
+
self.c_lib.DefaultSettings.restype = IDSettings
|
|
230
|
+
|
|
231
|
+
self.modeldir = str(modelpath)
|
|
232
|
+
# self.modelpath = ctypes.c_char_p(
|
|
233
|
+
# os.path.join(self.modeldir, "phase_model_8.bin").encode()
|
|
234
|
+
# )
|
|
235
|
+
# Create null pointer for model path
|
|
236
|
+
self.modelpath = None
|
|
237
|
+
self.config = IsoDecConfig()
|
|
238
|
+
# self.determine_model()
|
|
239
|
+
|
|
240
|
+
def encode(self, centroids, maxz=50, phaseres=8, config=None):
|
|
241
|
+
cmz = centroids[:, 0].astype(np.double)
|
|
242
|
+
cint = centroids[:, 1].astype(np.float32)
|
|
243
|
+
elen = maxz * phaseres
|
|
244
|
+
emat = np.zeros(elen).astype(np.float32)
|
|
245
|
+
idconf = IDConfig()
|
|
246
|
+
idconf.pres = phaseres
|
|
247
|
+
idconf.maxz = maxz
|
|
248
|
+
idconf.elen = elen
|
|
249
|
+
|
|
250
|
+
if config is not None:
|
|
251
|
+
self.config = config
|
|
252
|
+
settings = config_to_settings(self.config)
|
|
253
|
+
|
|
254
|
+
self.c_lib.encode(
|
|
255
|
+
cmz.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
|
|
256
|
+
cint.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
|
|
257
|
+
ctypes.c_int(len(cmz)),
|
|
258
|
+
emat.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
|
|
259
|
+
idconf,
|
|
260
|
+
settings,
|
|
261
|
+
)
|
|
262
|
+
# Convert emat to numpy
|
|
263
|
+
emat = np.ctypeslib.as_array(emat)
|
|
264
|
+
return emat
|
|
265
|
+
|
|
266
|
+
def predict_charge(self, centroids, config=None):
|
|
267
|
+
|
|
268
|
+
cmz = centroids[:, 0].astype(np.double)
|
|
269
|
+
cint = centroids[:, 1].astype(np.float32)
|
|
270
|
+
# charge = ctypes.c_int(0)
|
|
271
|
+
# self.modelpath = ctypes.c_char_p(None)
|
|
272
|
+
|
|
273
|
+
charge = self.c_lib.predict_charge(
|
|
274
|
+
cmz.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
|
|
275
|
+
cint.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
|
|
276
|
+
ctypes.c_int(len(cmz)),
|
|
277
|
+
self.modelpath,
|
|
278
|
+
# ctypes.byref(charge),
|
|
279
|
+
)
|
|
280
|
+
return charge
|
|
281
|
+
|
|
282
|
+
def process_spectrum(self, centroids, pks=None, config=None, input_type=None):
|
|
283
|
+
|
|
284
|
+
if input_type is None:
|
|
285
|
+
input_type = "Peptide"
|
|
286
|
+
type_c = ctypes.c_char_p(input_type.encode('utf-8'))
|
|
287
|
+
|
|
288
|
+
cmz = centroids[:, 0].astype(np.double)
|
|
289
|
+
cint = centroids[:, 1].astype(np.float32)
|
|
290
|
+
n = len(cmz)
|
|
291
|
+
|
|
292
|
+
if config is not None:
|
|
293
|
+
self.config = config
|
|
294
|
+
settings = config_to_settings(self.config)
|
|
295
|
+
|
|
296
|
+
elems = (MPStruct * n)()
|
|
297
|
+
matchedpeaks = ctypes.cast(elems, ctypes.POINTER(MPStruct))
|
|
298
|
+
|
|
299
|
+
nmatched = self.c_lib.process_spectrum(
|
|
300
|
+
cmz.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
|
|
301
|
+
cint.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
|
|
302
|
+
ctypes.c_int(n),
|
|
303
|
+
self.modelpath,
|
|
304
|
+
matchedpeaks,
|
|
305
|
+
settings,
|
|
306
|
+
type_c,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
if pks is None:
|
|
310
|
+
pks = MatchedCollection()
|
|
311
|
+
for p in matchedpeaks[:nmatched]:
|
|
312
|
+
if p.z == 0:
|
|
313
|
+
continue
|
|
314
|
+
|
|
315
|
+
# Extract peak centroid data
|
|
316
|
+
startindex = p.startindex
|
|
317
|
+
endindex = p.endindex
|
|
318
|
+
if startindex < 0 or endindex < 0 or endindex <= startindex:
|
|
319
|
+
raise ValueError("Invalid start or end index in matched peak.")
|
|
320
|
+
pkcent = centroids[startindex:endindex + 1]
|
|
321
|
+
|
|
322
|
+
pk = MatchedPeak(p.z, p.mz, p.avgmass, centroids=pkcent, config=self.config)
|
|
323
|
+
pk.monoiso = p.monoiso
|
|
324
|
+
pk.peakmass = p.peakmass
|
|
325
|
+
pk.avgmass = p.avgmass
|
|
326
|
+
|
|
327
|
+
monoisos = np.array(p.monoisos)
|
|
328
|
+
monoisos = monoisos[monoisos > 0]
|
|
329
|
+
pk.monoisos = monoisos
|
|
330
|
+
|
|
331
|
+
if config is not None:
|
|
332
|
+
pk.scan = config.activescan
|
|
333
|
+
pk.ms_order = config.activescanorder
|
|
334
|
+
pk.rt = config.activescanrt
|
|
335
|
+
|
|
336
|
+
isodist = np.array(p.isodist)
|
|
337
|
+
isomz = np.array(p.isomz)
|
|
338
|
+
isomass = np.array(p.isomass)
|
|
339
|
+
b1 = isodist > np.amax(isodist) * 0.001
|
|
340
|
+
isodist = isodist[b1]
|
|
341
|
+
isomz = isomz[b1]
|
|
342
|
+
isomass = isomass[b1]
|
|
343
|
+
pk.matchedintensity = np.sum(isodist)
|
|
344
|
+
pk.peakint = p.peakint
|
|
345
|
+
|
|
346
|
+
# pk.isodist = calc_isotope_dist(p.monoiso, p.z)
|
|
347
|
+
pk.isodist = np.transpose((isomz, isodist))
|
|
348
|
+
# pk.isodist[:, 1] = p.peakint
|
|
349
|
+
|
|
350
|
+
pk.massdist = np.transpose((isomass, isodist))
|
|
351
|
+
|
|
352
|
+
pk.startindex = p.startindex
|
|
353
|
+
pk.endindex = p.endindex
|
|
354
|
+
|
|
355
|
+
pks.add_peak(pk)
|
|
356
|
+
pks.add_pk_to_masses(pk, config=self.config)
|
|
357
|
+
return pks
|
|
358
|
+
|
|
359
|
+
def determine_model(self, default=True):
|
|
360
|
+
if default:
|
|
361
|
+
self.modelpath = None
|
|
362
|
+
else:
|
|
363
|
+
print("Model Directory:", self.modeldir)
|
|
364
|
+
if self.config.phaseres == 4:
|
|
365
|
+
self.modelpath = ctypes.c_char_p(
|
|
366
|
+
os.path.join(self.modeldir, "phase_model_4.bin").encode('utf-8')
|
|
367
|
+
)
|
|
368
|
+
elif self.config.phaseres == 8:
|
|
369
|
+
|
|
370
|
+
self.modelpath = ctypes.c_char_p(
|
|
371
|
+
os.path.join(self.modeldir, "phase_model_8.bin").encode('utf-8')
|
|
372
|
+
)
|
|
373
|
+
else:
|
|
374
|
+
print("Invalid phase resolution.", self.config.phaseres)
|
|
375
|
+
raise ValueError("Invalid phase resolution.")
|
|
376
|
+
|
|
377
|
+
if self.config.verbose:
|
|
378
|
+
print(
|
|
379
|
+
"Running C code with phaseres of:",
|
|
380
|
+
self.config.phaseres,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
if __name__ == "__main__":
|
|
385
|
+
eng = IsoDecWrapper()
|
|
386
|
+
#
|
|
387
|
+
# eng.config.phaseres = 4
|
|
388
|
+
# eng.encode(example)
|
|
389
|
+
# print(eng.predict_charge(example))
|
|
390
|
+
# dat = eng.process_spectrum(example)
|
|
391
|
+
#
|
|
392
|
+
# print(dat)
|
|
393
|
+
filepath = "C:\\Data\\IsoNN\\test2.txt"
|
|
394
|
+
spectrum = np.loadtxt(filepath, skiprows=0)
|
|
395
|
+
pks = eng.process_spectrum(spectrum)
|
|
396
|
+
print(len(pks.peaks))
|
|
397
|
+
|
|
398
|
+
# Find peak with monoiso near 6396
|
|
399
|
+
for pk in pks.masses:
|
|
400
|
+
if 6395 < pk.monoiso < 6397:
|
|
401
|
+
print("Found Peak:", pk)
|
|
402
|
+
cplot(pk.decon_centroids, color="k", factor=1)
|
|
403
|
+
cplot(pk.massdist, color="k", factor=-1)
|
|
404
|
+
import matplotlib.pyplot as plt
|
|
405
|
+
|
|
406
|
+
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
|
|
407
|
+
for i, p in enumerate(pk.clusters):
|
|
408
|
+
print(" Subpeak:", p)
|
|
409
|
+
# if p.monoiso > 6396:
|
|
410
|
+
# continue
|
|
411
|
+
cplot(p.decon_centroids, color=colors[i % len(colors)], factor=1)
|
|
412
|
+
cplot(p.massdist, color=colors[i % len(colors)], factor=-1)
|
|
413
|
+
plt.show()
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
exit()
|
|
417
|
+
# filepath = "C:\\Data\\IsoNN\\test2.txt"
|
|
418
|
+
filepath = "Z:\\Group Share\\JGP\\js8b05641_si_001\\etd_spectrum.txt"
|
|
419
|
+
spectrum = np.loadtxt(filepath, skiprows=0)
|
|
420
|
+
spectrum2 = datachop(spectrum, 891.195, 893.757)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
e1 = eng.encode(spectrum2)
|
|
424
|
+
print("Encoded:", e1.shape)
|
|
425
|
+
|
|
426
|
+
# z = eng.predict_charge(spectrum2)
|
|
427
|
+
# print("Predicted Charge:", z)
|
|
428
|
+
|
|
429
|
+
# exit()
|
|
430
|
+
pks = eng.process_spectrum(spectrum2)
|
|
431
|
+
print(len(pks.peaks))
|
|
432
|
+
# exit()
|
|
433
|
+
plot_pks(pks, centroids=spectrum, show=True, title="C Interface")
|
|
434
|
+
plt.show()
|