nmr-processing 9.1.0 → 9.2.0
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.
- package/lib/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/prediction/{prediction1D.d.ts → Prediction1D.d.ts} +0 -0
- package/lib/prediction/{prediction1D.js → Prediction1D.js} +1 -1
- package/lib/prediction/Prediction1D.js.map +1 -0
- package/lib/prediction/predictAll.d.ts +1 -1
- package/lib/prediction/predictAll.js.map +1 -1
- package/lib/prediction/predictAllSpectra.js +2 -2
- package/lib/prediction/predictAllSpectra.js.map +1 -1
- package/lib/prediction/predictCOSY.d.ts +1 -1
- package/lib/prediction/predictCOSY.js.map +1 -1
- package/lib/prediction/predictCarbon.d.ts +1 -1
- package/lib/prediction/predictHMBC.d.ts +1 -1
- package/lib/prediction/predictHMBC.js.map +1 -1
- package/lib/prediction/predictHSQC.d.ts +1 -1
- package/lib/prediction/predictHSQC.js.map +1 -1
- package/lib/prediction/predictProton.d.ts +1 -1
- package/lib/prediction/utils/fetchPrediction.d.ts +1 -1
- package/lib/prediction/utils/getPredictions.d.ts +1 -1
- package/lib/prediction/utils/predict2D.d.ts +1 -1
- package/lib/prediction/utils/predict2D.js +1 -1
- package/lib/prediction/utils/predict2D.js.map +1 -1
- package/lib/signals/{jcoupling.d.ts → Jcoupling.d.ts} +0 -0
- package/lib/signals/{jcoupling.js → Jcoupling.js} +1 -1
- package/lib/signals/Jcoupling.js.map +1 -0
- package/lib/signals/NMRSignal1D.d.ts +1 -1
- package/lib/signals/addDummySignals.js +12 -7
- package/lib/signals/addDummySignals.js.map +1 -1
- package/lib/signals/optimization/defaultParameters.d.ts +1 -0
- package/lib/signals/optimization/defaultParameters.js +36 -0
- package/lib/signals/optimization/defaultParameters.js.map +1 -0
- package/lib/signals/optimization/directOptimization.d.ts +20 -0
- package/lib/signals/optimization/directOptimization.js +32 -0
- package/lib/signals/optimization/directOptimization.js.map +1 -0
- package/lib/signals/optimization/getInternalSignals.d.ts +54 -0
- package/lib/signals/optimization/getInternalSignals.js +150 -0
- package/lib/signals/optimization/getInternalSignals.js.map +1 -0
- package/lib/signals/optimization/getSumOfShapes.d.ts +4 -0
- package/lib/signals/optimization/getSumOfShapes.js +46 -0
- package/lib/signals/optimization/getSumOfShapes.js.map +1 -0
- package/lib/signals/optimization/signalsToPointXY.d.ts +9 -0
- package/lib/signals/optimization/signalsToPointXY.js +24 -0
- package/lib/signals/optimization/signalsToPointXY.js.map +1 -0
- package/lib/signals/optimizeSignals.d.ts +40 -0
- package/lib/signals/optimizeSignals.js +108 -0
- package/lib/signals/optimizeSignals.js.map +1 -0
- package/lib/signals/signalsJoin.d.ts +1 -1
- package/lib/signals/simulation/simulateXYPeaks.js +1 -1
- package/lib/signals/simulation/simulateXYPeaks.js.map +1 -1
- package/lib/xy/xyAutoPeaksPicking.d.ts +2 -2
- package/lib/xy/xyAutoPeaksPicking.js +2 -2
- package/lib/xy/xyAutoPeaksPicking.js.map +1 -1
- package/lib/xy/xyPeaksOptimization.js +2 -1
- package/lib/xy/xyPeaksOptimization.js.map +1 -1
- package/lib-esm/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
- package/lib-esm/index.js +1 -0
- package/lib-esm/index.js.map +1 -1
- package/lib-esm/prediction/Prediction1D.js +2 -0
- package/lib-esm/prediction/Prediction1D.js.map +1 -0
- package/lib-esm/prediction/predictAll.js.map +1 -1
- package/lib-esm/prediction/predictAllSpectra.js +2 -2
- package/lib-esm/prediction/predictAllSpectra.js.map +1 -1
- package/lib-esm/prediction/predictCOSY.js.map +1 -1
- package/lib-esm/prediction/predictHMBC.js.map +1 -1
- package/lib-esm/prediction/predictHSQC.js.map +1 -1
- package/lib-esm/prediction/utils/predict2D.js +1 -1
- package/lib-esm/prediction/utils/predict2D.js.map +1 -1
- package/lib-esm/signals/Jcoupling.js +2 -0
- package/lib-esm/signals/Jcoupling.js.map +1 -0
- package/lib-esm/signals/addDummySignals.js +12 -7
- package/lib-esm/signals/addDummySignals.js.map +1 -1
- package/lib-esm/signals/optimization/defaultParameters.js +33 -0
- package/lib-esm/signals/optimization/defaultParameters.js.map +1 -0
- package/lib-esm/signals/optimization/directOptimization.js +25 -0
- package/lib-esm/signals/optimization/directOptimization.js.map +1 -0
- package/lib-esm/signals/optimization/getInternalSignals.js +146 -0
- package/lib-esm/signals/optimization/getInternalSignals.js.map +1 -0
- package/lib-esm/signals/optimization/getSumOfShapes.js +42 -0
- package/lib-esm/signals/optimization/getSumOfShapes.js.map +1 -0
- package/lib-esm/signals/optimization/signalsToPointXY.js +20 -0
- package/lib-esm/signals/optimization/signalsToPointXY.js.map +1 -0
- package/lib-esm/signals/optimizeSignals.js +104 -0
- package/lib-esm/signals/optimizeSignals.js.map +1 -0
- package/lib-esm/signals/simulation/simulateXYPeaks.js +1 -1
- package/lib-esm/signals/simulation/simulateXYPeaks.js.map +1 -1
- package/lib-esm/xy/xyAutoPeaksPicking.js +3 -3
- package/lib-esm/xy/xyAutoPeaksPicking.js.map +1 -1
- package/lib-esm/xy/xyPeaksOptimization.js +2 -1
- package/lib-esm/xy/xyPeaksOptimization.js.map +1 -1
- package/package.json +3 -2
- package/src/assignment/utils/getAssignment/buildAssignments.ts +1 -1
- package/src/index.ts +3 -2
- package/src/prediction/{prediction1D.ts → Prediction1D.ts} +0 -0
- package/src/prediction/predictAll.ts +1 -1
- package/src/prediction/predictAllSpectra.ts +2 -2
- package/src/prediction/predictCOSY.ts +1 -1
- package/src/prediction/predictCarbon.ts +1 -1
- package/src/prediction/predictHMBC.ts +1 -1
- package/src/prediction/predictHSQC.ts +1 -1
- package/src/prediction/predictProton.ts +1 -1
- package/src/prediction/utils/fetchPrediction.ts +1 -1
- package/src/prediction/utils/getPredictions.ts +1 -1
- package/src/prediction/utils/predict2D.ts +2 -2
- package/src/signal/signalJoinCouplings.ts +1 -1
- package/src/signals/{jcoupling.ts → Jcoupling.ts} +0 -0
- package/src/signals/NMRSignal1D.ts +1 -1
- package/src/signals/addDummySignals.ts +17 -7
- package/src/signals/optimization/defaultParameters.ts +45 -0
- package/src/signals/optimization/directOptimization.ts +49 -0
- package/src/signals/optimization/getInternalSignals.ts +287 -0
- package/src/signals/optimization/getSumOfShapes.ts +54 -0
- package/src/signals/optimization/signalsToPointXY.ts +37 -0
- package/src/signals/optimizeSignals.ts +173 -0
- package/src/signals/signalsJoin.ts +1 -1
- package/src/signals/signalsToXY.ts +1 -1
- package/src/signals/simulation/signalsToSpinSystem.ts +1 -1
- package/src/signals/simulation/simulateXYPeaks.ts +1 -2
- package/src/xy/xyAutoPeaksPicking.ts +10 -6
- package/src/xy/xyPeaksOptimization.ts +3 -1
- package/lib/prediction/prediction1D.js.map +0 -1
- package/lib/signals/jcoupling.js.map +0 -1
- package/lib-esm/prediction/prediction1D.js +0 -2
- package/lib-esm/prediction/prediction1D.js.map +0 -1
- package/lib-esm/signals/jcoupling.js +0 -2
- package/lib-esm/signals/jcoupling.js.map +0 -1
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { DataXY } from 'cheminfo-types';
|
|
2
|
+
import { getShape1D, Shape1D, Shape1DInstance } from 'ml-peak-shape-generator';
|
|
3
|
+
import { xyFindClosestPoint } from 'ml-spectra-processing';
|
|
4
|
+
|
|
5
|
+
import { MakeMandatory } from '../../utilities/MakeMandatory';
|
|
6
|
+
import { Jcoupling } from '../Jcoupling';
|
|
7
|
+
import { Signal } from '../optimizeSignals';
|
|
8
|
+
|
|
9
|
+
import { defaultParameters } from './defaultParameters';
|
|
10
|
+
import { signalsToPointXY } from './signalsToPointXY';
|
|
11
|
+
|
|
12
|
+
const properties = ['init', 'min', 'max', 'gradientDifference'];
|
|
13
|
+
|
|
14
|
+
export interface InternalSignal {
|
|
15
|
+
shape: Shape1D;
|
|
16
|
+
shapeFct: Shape1DInstance;
|
|
17
|
+
signal: MakeMandatory<Signal, 'js' | 'intensity'>;
|
|
18
|
+
parameters: string[];
|
|
19
|
+
propertiesValues: Record<string, number[]>;
|
|
20
|
+
fromIndex: number;
|
|
21
|
+
toIndex: number;
|
|
22
|
+
fromIndexCoupling: number;
|
|
23
|
+
toIndexCoupling: number;
|
|
24
|
+
}
|
|
25
|
+
export type Parameter = 'delta' | 'intensity' | 'fwhm' | 'mu' | 'coupling';
|
|
26
|
+
|
|
27
|
+
export type ParametersFromOptions = Record<
|
|
28
|
+
Parameter,
|
|
29
|
+
Record<
|
|
30
|
+
string,
|
|
31
|
+
| number
|
|
32
|
+
| ((options?: {
|
|
33
|
+
signal?: Signal;
|
|
34
|
+
shape?: Shape1D;
|
|
35
|
+
index?: number;
|
|
36
|
+
jCoupling?: Jcoupling;
|
|
37
|
+
}) => number)
|
|
38
|
+
>
|
|
39
|
+
>;
|
|
40
|
+
export interface GetInternalSignalsOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Initial line width in Hz
|
|
43
|
+
* @default 1
|
|
44
|
+
*/
|
|
45
|
+
lineWidth?: number;
|
|
46
|
+
/**
|
|
47
|
+
* frequency
|
|
48
|
+
*/
|
|
49
|
+
frequency: number;
|
|
50
|
+
baseline?: number;
|
|
51
|
+
shape?: Shape1D;
|
|
52
|
+
parameters?: ParametersFromOptions;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type Shape1DWithFWHM = Omit<Shape1D, 'fwhm'> & { fwhm: number };
|
|
56
|
+
export function getInternalSignals(
|
|
57
|
+
data: DataXY,
|
|
58
|
+
signals: Signal[],
|
|
59
|
+
minMaxY: Record<string, number>,
|
|
60
|
+
options: GetInternalSignalsOptions,
|
|
61
|
+
) {
|
|
62
|
+
let index = 0;
|
|
63
|
+
let internalSignals = [];
|
|
64
|
+
const {
|
|
65
|
+
shape: shapeAsOption = { kind: 'gaussian' },
|
|
66
|
+
baseline: shiftValue = minMaxY.min,
|
|
67
|
+
lineWidth = 1,
|
|
68
|
+
frequency,
|
|
69
|
+
} = options;
|
|
70
|
+
|
|
71
|
+
const normalizedSignals = normalizeSignals({
|
|
72
|
+
signals,
|
|
73
|
+
data,
|
|
74
|
+
shiftValue,
|
|
75
|
+
frequency,
|
|
76
|
+
range: minMaxY.range,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
for (const signal of normalizedSignals) {
|
|
80
|
+
const { shape = { ...shapeAsOption } } = signal;
|
|
81
|
+
|
|
82
|
+
if (!shape.fwhm) {
|
|
83
|
+
shape.fwhm = lineWidth;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
shape.fwhm /= frequency;
|
|
87
|
+
const shapeFct = getShape1D(shape);
|
|
88
|
+
|
|
89
|
+
const parameters: Parameter[] = [
|
|
90
|
+
'delta',
|
|
91
|
+
'intensity',
|
|
92
|
+
...shapeFct.getParameters(),
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const propertiesValues: Record<string, number[]> = {
|
|
96
|
+
min: [],
|
|
97
|
+
max: [],
|
|
98
|
+
init: [],
|
|
99
|
+
gradientDifference: [],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
for (const parameter of parameters) {
|
|
103
|
+
for (const property of properties) {
|
|
104
|
+
propertiesValues[property].push(
|
|
105
|
+
getPropertyValue({
|
|
106
|
+
signal,
|
|
107
|
+
frequency,
|
|
108
|
+
parameter,
|
|
109
|
+
property,
|
|
110
|
+
minMaxY,
|
|
111
|
+
shapeFct,
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (const jCoupling of signal.js || []) {
|
|
118
|
+
for (const property of properties) {
|
|
119
|
+
const parameter = 'coupling' as Parameter;
|
|
120
|
+
let generalParameterValue =
|
|
121
|
+
options?.parameters?.[parameter]?.[property];
|
|
122
|
+
if (generalParameterValue) {
|
|
123
|
+
if (typeof generalParameterValue === 'number') {
|
|
124
|
+
propertiesValues[property].push(
|
|
125
|
+
getNormalizedValue(
|
|
126
|
+
generalParameterValue,
|
|
127
|
+
parameter,
|
|
128
|
+
property,
|
|
129
|
+
minMaxY,
|
|
130
|
+
frequency,
|
|
131
|
+
options.baseline,
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
} else {
|
|
135
|
+
let value = generalParameterValue({ jCoupling });
|
|
136
|
+
propertiesValues[property].push(
|
|
137
|
+
getNormalizedValue(
|
|
138
|
+
value,
|
|
139
|
+
parameter,
|
|
140
|
+
property,
|
|
141
|
+
minMaxY,
|
|
142
|
+
frequency,
|
|
143
|
+
options.baseline,
|
|
144
|
+
),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (!defaultParameters[parameter]) {
|
|
149
|
+
throw new Error(`No default parameter for ${parameter}`);
|
|
150
|
+
}
|
|
151
|
+
const defaultParameterValues = defaultParameters[parameter][property];
|
|
152
|
+
propertiesValues[property].push(defaultParameterValues({ jCoupling }));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const fromIndex = index;
|
|
157
|
+
const toIndex = fromIndex + parameters.length - 1;
|
|
158
|
+
|
|
159
|
+
const fromIndexCoupling = toIndex + 1;
|
|
160
|
+
const toIndexCoupling = fromIndexCoupling + signal.js.length - 1;
|
|
161
|
+
|
|
162
|
+
index += toIndexCoupling - fromIndex + 1;
|
|
163
|
+
internalSignals.push({
|
|
164
|
+
shape: shape as Shape1DWithFWHM,
|
|
165
|
+
shapeFct,
|
|
166
|
+
signal,
|
|
167
|
+
parameters,
|
|
168
|
+
propertiesValues,
|
|
169
|
+
fromIndex,
|
|
170
|
+
toIndex,
|
|
171
|
+
fromIndexCoupling,
|
|
172
|
+
toIndexCoupling,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return internalSignals;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function normalizeSignals<T extends Signal>(options: {
|
|
180
|
+
signals: T[];
|
|
181
|
+
data: DataXY;
|
|
182
|
+
shiftValue: number;
|
|
183
|
+
frequency: number;
|
|
184
|
+
range: number;
|
|
185
|
+
}) {
|
|
186
|
+
const { signals, data, frequency, shiftValue, range } = options;
|
|
187
|
+
|
|
188
|
+
const getMaxIntensity = (signal: Signal) => {
|
|
189
|
+
const peaks = signalsToPointXY([signal], { frequency, maxClusterSize: 1 });
|
|
190
|
+
const biggestPeak = peaks.reduce(
|
|
191
|
+
(biggest, peak) => (peak.y > biggest.y ? peak : biggest),
|
|
192
|
+
peaks[0],
|
|
193
|
+
);
|
|
194
|
+
const { y: maxIntensity } = xyFindClosestPoint(data, biggestPeak.x);
|
|
195
|
+
return maxIntensity;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return signals.map((signal) => {
|
|
199
|
+
const { intensity = getMaxIntensity(signal) } = signal;
|
|
200
|
+
return {
|
|
201
|
+
...signal,
|
|
202
|
+
js: signal.js || [],
|
|
203
|
+
intensity: (intensity - shiftValue) / range,
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function getPropertyValue(options: {
|
|
209
|
+
signal: Signal;
|
|
210
|
+
parameter: Parameter;
|
|
211
|
+
property: string;
|
|
212
|
+
minMaxY: Record<string, number>;
|
|
213
|
+
shapeFct: Shape1DInstance;
|
|
214
|
+
frequency: number;
|
|
215
|
+
baseline?: number;
|
|
216
|
+
parameters?: ParametersFromOptions;
|
|
217
|
+
}) {
|
|
218
|
+
const { signal, frequency, parameter, property, minMaxY, shapeFct } = options;
|
|
219
|
+
|
|
220
|
+
// check if the property is specified in the signal
|
|
221
|
+
let propertyValue = signal?.parameters?.[parameter]?.[property];
|
|
222
|
+
if (propertyValue) {
|
|
223
|
+
return getNormalizedValue(
|
|
224
|
+
propertyValue,
|
|
225
|
+
parameter,
|
|
226
|
+
property,
|
|
227
|
+
minMaxY,
|
|
228
|
+
frequency,
|
|
229
|
+
options.baseline,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// check if there are some global option, it could be a number or a callback
|
|
234
|
+
|
|
235
|
+
let generalParameterValue = options?.parameters?.[parameter]?.[property];
|
|
236
|
+
if (generalParameterValue) {
|
|
237
|
+
if (typeof generalParameterValue === 'number') {
|
|
238
|
+
return getNormalizedValue(
|
|
239
|
+
generalParameterValue,
|
|
240
|
+
parameter,
|
|
241
|
+
property,
|
|
242
|
+
minMaxY,
|
|
243
|
+
frequency,
|
|
244
|
+
options.baseline,
|
|
245
|
+
);
|
|
246
|
+
} else {
|
|
247
|
+
let value = generalParameterValue(signal);
|
|
248
|
+
return getNormalizedValue(
|
|
249
|
+
value,
|
|
250
|
+
parameter,
|
|
251
|
+
property,
|
|
252
|
+
minMaxY,
|
|
253
|
+
frequency,
|
|
254
|
+
options.baseline,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// we just need to take the default parameters
|
|
260
|
+
if (!defaultParameters[parameter]) {
|
|
261
|
+
throw new Error(`No default parameter for ${parameter}`);
|
|
262
|
+
}
|
|
263
|
+
const defaultParameterValues = defaultParameters[parameter][property];
|
|
264
|
+
return defaultParameterValues({ signal, shape: shapeFct });
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getNormalizedValue(
|
|
268
|
+
value: number,
|
|
269
|
+
parameter: keyof ParametersFromOptions,
|
|
270
|
+
property: string,
|
|
271
|
+
minMaxY: Record<string, number>,
|
|
272
|
+
frequency: number,
|
|
273
|
+
baseline?: number,
|
|
274
|
+
) {
|
|
275
|
+
if (parameter === 'intensity') {
|
|
276
|
+
if (property === 'gradientDifference') {
|
|
277
|
+
return value;
|
|
278
|
+
} else {
|
|
279
|
+
return baseline !== undefined
|
|
280
|
+
? (value - baseline) / minMaxY.range
|
|
281
|
+
: (value - minMaxY.min) / minMaxY.range;
|
|
282
|
+
}
|
|
283
|
+
} else if (parameter === 'fwhm') {
|
|
284
|
+
return value / frequency;
|
|
285
|
+
}
|
|
286
|
+
return value;
|
|
287
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { InternalSignal } from './getInternalSignals';
|
|
2
|
+
import { signalsToPointXY, SignalsToPointXYOptions } from './signalsToPointXY';
|
|
3
|
+
|
|
4
|
+
export type SumOfShapes = (parameters: number[]) => (x: number) => number;
|
|
5
|
+
|
|
6
|
+
const defaultSimulationOptions: Partial<SignalsToPointXYOptions> = {
|
|
7
|
+
maxClusterSize: 1,
|
|
8
|
+
};
|
|
9
|
+
export function getSumOfShapes(
|
|
10
|
+
internalSignals: InternalSignal[],
|
|
11
|
+
options: SignalsToPointXYOptions,
|
|
12
|
+
) {
|
|
13
|
+
const simulationOptions = { ...defaultSimulationOptions, ...options };
|
|
14
|
+
return function sumOfShapes(parameters: number[]) {
|
|
15
|
+
const peaks: any = [];
|
|
16
|
+
for (const internalSignal of internalSignals) {
|
|
17
|
+
const delta = parameters[internalSignal.fromIndex];
|
|
18
|
+
const intensity = parameters[internalSignal.fromIndex + 1];
|
|
19
|
+
for (let i = 2; i < internalSignal.parameters.length; i++) {
|
|
20
|
+
//@ts-expect-error Not simply to solve the issue
|
|
21
|
+
internalSignal.shapeFct[internalSignal.parameters[i]] =
|
|
22
|
+
parameters[internalSignal.fromIndex + i];
|
|
23
|
+
}
|
|
24
|
+
const couplings = internalSignal.signal.js;
|
|
25
|
+
for (let i = 0; i < couplings.length; i++) {
|
|
26
|
+
couplings[i].coupling =
|
|
27
|
+
parameters[internalSignal.fromIndexCoupling + i];
|
|
28
|
+
}
|
|
29
|
+
const currentPeaks = signalsToPointXY(
|
|
30
|
+
[
|
|
31
|
+
{
|
|
32
|
+
delta,
|
|
33
|
+
js: couplings,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
simulationOptions,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
peaks.push(
|
|
40
|
+
...currentPeaks.map((peak) => {
|
|
41
|
+
return { intensity, shape: internalSignal.shapeFct, ...peak };
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return (currentX: number) => {
|
|
46
|
+
let totalY = 0;
|
|
47
|
+
for (let peak of peaks) {
|
|
48
|
+
const { x, y, intensity, shape } = peak;
|
|
49
|
+
totalY += y * intensity * shape.fct(currentX - x);
|
|
50
|
+
}
|
|
51
|
+
return totalY;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { NMRSignal1D } from '../NMRSignal1D';
|
|
2
|
+
import { addDummySignals } from '../addDummySignals';
|
|
3
|
+
import { signalsToSpinSystem } from '../simulation/signalsToSpinSystem';
|
|
4
|
+
import { simulateXYPeaks } from '../simulation/simulateXYPeaks';
|
|
5
|
+
import { splitSpinSystem } from '../simulation/splitSpinSystem';
|
|
6
|
+
|
|
7
|
+
export interface SignalsToPointXYOptions {
|
|
8
|
+
frequency: number;
|
|
9
|
+
maxClusterSize?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function signalsToPointXY(
|
|
13
|
+
signals: NMRSignal1D[],
|
|
14
|
+
options: SignalsToPointXYOptions,
|
|
15
|
+
) {
|
|
16
|
+
const { frequency, maxClusterSize = 1 } = options;
|
|
17
|
+
const completeSignalSet = addDummySignals(signals);
|
|
18
|
+
|
|
19
|
+
const spinSystem = signalsToSpinSystem(completeSignalSet);
|
|
20
|
+
spinSystem.clusters = splitSpinSystem(spinSystem, {
|
|
21
|
+
frequency,
|
|
22
|
+
maxClusterSize,
|
|
23
|
+
});
|
|
24
|
+
const pointsXY = simulateXYPeaks(spinSystem, options).filter(
|
|
25
|
+
(point) => point.x < 1000,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const maxIntensity = pointsXY.reduce(
|
|
29
|
+
(max, peak) => (peak.y > max ? peak.y : max),
|
|
30
|
+
Number.MIN_SAFE_INTEGER,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
for (const point of pointsXY) {
|
|
34
|
+
point.y /= maxIntensity;
|
|
35
|
+
}
|
|
36
|
+
return pointsXY;
|
|
37
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { v4 } from '@lukeed/uuid';
|
|
2
|
+
import { DataXY } from 'cheminfo-types';
|
|
3
|
+
import { levenbergMarquardt } from 'ml-levenberg-marquardt';
|
|
4
|
+
import { getShape1D, Shape1D } from 'ml-peak-shape-generator';
|
|
5
|
+
import { xMinMaxValues } from 'ml-spectra-processing';
|
|
6
|
+
|
|
7
|
+
import { NMRPeak1D } from '../peaks/NMRPeak1D';
|
|
8
|
+
|
|
9
|
+
import { NMRSignal1D } from './NMRSignal1D';
|
|
10
|
+
import { directOptimization } from './optimization/directOptimization';
|
|
11
|
+
import {
|
|
12
|
+
getInternalSignals,
|
|
13
|
+
ParametersFromOptions,
|
|
14
|
+
Shape1DWithFWHM,
|
|
15
|
+
} from './optimization/getInternalSignals';
|
|
16
|
+
import { getSumOfShapes } from './optimization/getSumOfShapes';
|
|
17
|
+
import {
|
|
18
|
+
signalsToPointXY,
|
|
19
|
+
SignalsToPointXYOptions,
|
|
20
|
+
} from './optimization/signalsToPointXY';
|
|
21
|
+
|
|
22
|
+
const defaultLMOptimizationOptions = {
|
|
23
|
+
damping: 1.5,
|
|
24
|
+
maxIterations: 100,
|
|
25
|
+
errorTolerance: 1e-8,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const defaultDirectOptimizationOptions = {
|
|
29
|
+
iterations: 25,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type ParametersFromSignal = Record<string, Record<string, number>>;
|
|
33
|
+
|
|
34
|
+
export interface Signal extends NMRSignal1D {
|
|
35
|
+
intensity?: number;
|
|
36
|
+
shape?: Shape1D;
|
|
37
|
+
parameters?: ParametersFromSignal;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface NMRPeak1DFull extends NMRPeak1D {
|
|
41
|
+
id: string;
|
|
42
|
+
shape: Shape1DWithFWHM;
|
|
43
|
+
width: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface OptimizeSignalsOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Initial line width in Hz
|
|
49
|
+
* @default 1
|
|
50
|
+
*/
|
|
51
|
+
lineWidth?: number;
|
|
52
|
+
baseline?: number;
|
|
53
|
+
shape?: Shape1D;
|
|
54
|
+
parameters?: ParametersFromOptions;
|
|
55
|
+
optimization?: any;
|
|
56
|
+
simulation: SignalsToPointXYOptions;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function optimizeSignals(
|
|
60
|
+
data: DataXY,
|
|
61
|
+
signals: Signal[],
|
|
62
|
+
options: OptimizeSignalsOptions,
|
|
63
|
+
) {
|
|
64
|
+
const {
|
|
65
|
+
optimization = {},
|
|
66
|
+
simulation: simulationOptions,
|
|
67
|
+
...restOptions
|
|
68
|
+
} = options;
|
|
69
|
+
let temp = xMinMaxValues(data.y);
|
|
70
|
+
const minMaxY = { ...temp, range: temp.max - temp.min };
|
|
71
|
+
|
|
72
|
+
const internalSignals = getInternalSignals(data, signals, minMaxY, {
|
|
73
|
+
frequency: simulationOptions.frequency,
|
|
74
|
+
...restOptions,
|
|
75
|
+
});
|
|
76
|
+
const { baseline: shiftValue = minMaxY.min } = options;
|
|
77
|
+
let normalizedY = new Float64Array(data.y.length);
|
|
78
|
+
for (let i = 0; i < data.y.length; i++) {
|
|
79
|
+
normalizedY[i] = (data.y[i] - shiftValue) / minMaxY.range;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const nbParams =
|
|
83
|
+
internalSignals[internalSignals.length - 1].toIndexCoupling + 1;
|
|
84
|
+
const minValues = new Float64Array(nbParams);
|
|
85
|
+
const maxValues = new Float64Array(nbParams);
|
|
86
|
+
const initialValues = new Float64Array(nbParams);
|
|
87
|
+
const gradientDifferences = new Float64Array(nbParams);
|
|
88
|
+
let index = 0;
|
|
89
|
+
for (const internalSignal of internalSignals) {
|
|
90
|
+
for (let i = 0; i < internalSignal.parameters.length; i++) {
|
|
91
|
+
minValues[index] = internalSignal.propertiesValues.min[i];
|
|
92
|
+
maxValues[index] = internalSignal.propertiesValues.max[i];
|
|
93
|
+
initialValues[index] = internalSignal.propertiesValues.init[i];
|
|
94
|
+
gradientDifferences[index++] =
|
|
95
|
+
internalSignal.propertiesValues.gradientDifference[i];
|
|
96
|
+
}
|
|
97
|
+
for (
|
|
98
|
+
let i = internalSignal.parameters.length;
|
|
99
|
+
i < internalSignal.parameters.length + internalSignal.signal.js.length;
|
|
100
|
+
i++
|
|
101
|
+
) {
|
|
102
|
+
minValues[index] = internalSignal.propertiesValues.min[i];
|
|
103
|
+
maxValues[index] = internalSignal.propertiesValues.max[i];
|
|
104
|
+
initialValues[index] = internalSignal.propertiesValues.init[i];
|
|
105
|
+
gradientDifferences[index++] =
|
|
106
|
+
internalSignal.propertiesValues.gradientDifference[i];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const sumOfShapes = getSumOfShapes(internalSignals, simulationOptions);
|
|
110
|
+
|
|
111
|
+
const { kind, options: optimizationOptions } = optimization;
|
|
112
|
+
|
|
113
|
+
const [algorithm, defaultOptimizationOptions] =
|
|
114
|
+
kind === 'direct'
|
|
115
|
+
? [directOptimization, defaultDirectOptimizationOptions]
|
|
116
|
+
: [levenbergMarquardt, defaultLMOptimizationOptions];
|
|
117
|
+
|
|
118
|
+
let fitted = algorithm({ x: data.x, y: normalizedY }, sumOfShapes, {
|
|
119
|
+
minValues,
|
|
120
|
+
maxValues,
|
|
121
|
+
initialValues,
|
|
122
|
+
gradientDifference: gradientDifferences,
|
|
123
|
+
...defaultOptimizationOptions,
|
|
124
|
+
...optimizationOptions,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const fittedValues = fitted.parameterValues;
|
|
128
|
+
const newSignals = [];
|
|
129
|
+
|
|
130
|
+
for (const internalSignal of internalSignals) {
|
|
131
|
+
const { fromIndexCoupling } = internalSignal;
|
|
132
|
+
const js = internalSignal.signal.js.map((jCoupling, i) => {
|
|
133
|
+
jCoupling.coupling = fittedValues[fromIndexCoupling + i];
|
|
134
|
+
return jCoupling;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const delta = fittedValues[internalSignal.fromIndex];
|
|
138
|
+
const pointXYs = signalsToPointXY([{ delta, js }], simulationOptions);
|
|
139
|
+
|
|
140
|
+
const intensity =
|
|
141
|
+
fittedValues[internalSignal.fromIndex + 1] * minMaxY.range + shiftValue;
|
|
142
|
+
|
|
143
|
+
const newPeaks: NMRPeak1DFull[] = [];
|
|
144
|
+
for (const { x, y } of pointXYs) {
|
|
145
|
+
const peak = {
|
|
146
|
+
id: v4(),
|
|
147
|
+
x,
|
|
148
|
+
y: intensity * y,
|
|
149
|
+
width: 0,
|
|
150
|
+
shape: { ...internalSignal.shape },
|
|
151
|
+
};
|
|
152
|
+
for (let i = 2; i < internalSignal.parameters.length; i++) {
|
|
153
|
+
//@ts-expect-error should be fixed once
|
|
154
|
+
peak.shape[internalSignal.parameters[i]] =
|
|
155
|
+
fittedValues[internalSignal.fromIndex + i];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
peak.shape.fwhm *= simulationOptions.frequency;
|
|
159
|
+
peak.width = getShape1D(peak.shape).fwhmToWidth(peak.shape.fwhm);
|
|
160
|
+
newPeaks.push(peak);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
newSignals.push({
|
|
164
|
+
delta,
|
|
165
|
+
js,
|
|
166
|
+
shape: { ...newPeaks[0].shape },
|
|
167
|
+
intensity,
|
|
168
|
+
peaks: newPeaks,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return newSignals;
|
|
173
|
+
}
|
|
@@ -6,8 +6,8 @@ import { signalJoinCouplings } from '../signal/signalJoinCouplings';
|
|
|
6
6
|
import type { SignalJoinCouplingsOptions } from '../signal/signalJoinCouplings';
|
|
7
7
|
import type { MakeMandatory } from '../utilities/MakeMandatory';
|
|
8
8
|
|
|
9
|
+
import type { Jcoupling } from './Jcoupling';
|
|
9
10
|
import type { NMRSignal1D } from './NMRSignal1D';
|
|
10
|
-
import type { Jcoupling } from './jcoupling';
|
|
11
11
|
|
|
12
12
|
export interface SignalsJoinOptions {
|
|
13
13
|
/**
|
|
@@ -4,8 +4,8 @@ import { xSequentialFill } from 'ml-spectra-processing';
|
|
|
4
4
|
|
|
5
5
|
import type { MakeMandatory } from '../utilities/MakeMandatory';
|
|
6
6
|
|
|
7
|
+
import type { Jcoupling } from './Jcoupling';
|
|
7
8
|
import type { NMRSignal1D } from './NMRSignal1D';
|
|
8
|
-
import type { Jcoupling } from './jcoupling';
|
|
9
9
|
import { signalsToSpinSystem } from './simulation/signalsToSpinSystem';
|
|
10
10
|
import simulate1D from './simulation/simulate1D';
|
|
11
11
|
import { splitSpinSystem } from './simulation/splitSpinSystem';
|
|
@@ -2,8 +2,8 @@ import { Matrix } from 'ml-matrix';
|
|
|
2
2
|
import simpleClustering from 'ml-simple-clustering';
|
|
3
3
|
|
|
4
4
|
import type { MakeMandatory } from '../../utilities/MakeMandatory';
|
|
5
|
+
import type { Jcoupling } from '../Jcoupling';
|
|
5
6
|
import type { NMRSignal1D } from '../NMRSignal1D';
|
|
6
|
-
import type { Jcoupling } from '../jcoupling';
|
|
7
7
|
import type { SpinSystem } from '../spinSystem';
|
|
8
8
|
|
|
9
9
|
interface Ids {
|
|
@@ -207,7 +207,6 @@ export function simulateXYPeaks(
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
const numFreq = frequencies.length;
|
|
210
|
-
|
|
211
210
|
if (numFreq > 0) {
|
|
212
211
|
weight /= sumI;
|
|
213
212
|
const diff = lineWidth / 64;
|
|
@@ -237,7 +236,7 @@ export function simulateXYPeaks(
|
|
|
237
236
|
}
|
|
238
237
|
}
|
|
239
238
|
|
|
240
|
-
return xyPeaks;
|
|
239
|
+
return xyPeaks.filter((peak) => peak.x < 1000);
|
|
241
240
|
}
|
|
242
241
|
|
|
243
242
|
function triuTimesAbs(A: SparseMatrix, val: number) {
|
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
OptimizePeaksOptions,
|
|
9
9
|
JoinBroadPeaksOptions,
|
|
10
10
|
GSDPeakOptimizedID,
|
|
11
|
+
setShape,
|
|
11
12
|
} from 'ml-gsd';
|
|
13
|
+
import { Shape1D } from 'ml-peak-shape-generator';
|
|
12
14
|
import {
|
|
13
15
|
xyExtract,
|
|
14
16
|
xNoiseSanPlot,
|
|
@@ -77,12 +79,13 @@ export interface OptionsXYAutoPeaksPicking extends Partial<GetPeakListOptions> {
|
|
|
77
79
|
frequency: number;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
type GSDPeakShapeID = GSDPeakID & { shape: Shape1D };
|
|
83
|
+
export type NMRPeak1DWithShapeID = MakeMandatory<NMRPeak1D, 'id' | 'shape'>;
|
|
81
84
|
|
|
82
85
|
export function xyAutoPeaksPicking(
|
|
83
86
|
data: DataXY,
|
|
84
87
|
options: OptionsXYAutoPeaksPicking,
|
|
85
|
-
):
|
|
88
|
+
): NMRPeak1DWithShapeID[] {
|
|
86
89
|
const {
|
|
87
90
|
from,
|
|
88
91
|
to,
|
|
@@ -177,8 +180,7 @@ function getPeakList(data: DataXY, options: GetPeakListOptions) {
|
|
|
177
180
|
realTopDetection,
|
|
178
181
|
} = options;
|
|
179
182
|
|
|
180
|
-
|
|
181
|
-
shape,
|
|
183
|
+
const peaks = gsd(data, {
|
|
182
184
|
sgOptions,
|
|
183
185
|
maxCriteria,
|
|
184
186
|
minMaxRatio,
|
|
@@ -187,6 +189,8 @@ function getPeakList(data: DataXY, options: GetPeakListOptions) {
|
|
|
187
189
|
realTopDetection,
|
|
188
190
|
});
|
|
189
191
|
|
|
192
|
+
const peakList = setShape(peaks, { output: peaks, shape });
|
|
193
|
+
|
|
190
194
|
const newPeaks = broadWidth
|
|
191
195
|
? joinBroadPeaks(peakList, {
|
|
192
196
|
broadRatio,
|
|
@@ -226,8 +230,8 @@ function getCutOff(data: number[] | Float64Array, options: OptionsGetCutOff) {
|
|
|
226
230
|
}
|
|
227
231
|
|
|
228
232
|
function toNMRPeak1DStructure(
|
|
229
|
-
peak:
|
|
230
|
-
):
|
|
233
|
+
peak: GSDPeakShapeID | GSDPeakOptimizedID,
|
|
234
|
+
): NMRPeak1DWithShapeID {
|
|
231
235
|
const { id, shape, x, y, width } = peak;
|
|
232
236
|
return {
|
|
233
237
|
id,
|
|
@@ -10,6 +10,7 @@ export interface XYPeaksOptimizationOptions extends OptimizePeaksOptions {
|
|
|
10
10
|
*/
|
|
11
11
|
frequency: number;
|
|
12
12
|
}
|
|
13
|
+
|
|
13
14
|
export function xyPeaksOptimization<T extends PeakXYWidth>(
|
|
14
15
|
data: DataXY,
|
|
15
16
|
peaks: T[],
|
|
@@ -18,8 +19,9 @@ export function xyPeaksOptimization<T extends PeakXYWidth>(
|
|
|
18
19
|
const { frequency } = options;
|
|
19
20
|
const newPeaks = convertWidthToPPM(peaks, { frequency });
|
|
20
21
|
const optimizedPeaks = optimizePeaks(data, newPeaks, options);
|
|
21
|
-
|
|
22
|
+
const result = convertWidthToHz(optimizedPeaks, {
|
|
22
23
|
frequency,
|
|
23
24
|
output: optimizedPeaks,
|
|
24
25
|
});
|
|
26
|
+
return result;
|
|
25
27
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prediction1D.js","sourceRoot":"","sources":["../../src/prediction/prediction1D.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jcoupling.js","sourceRoot":"","sources":["../../src/signals/jcoupling.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prediction1D.js","sourceRoot":"","sources":["../../src/prediction/prediction1D.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jcoupling.js","sourceRoot":"","sources":["../../src/signals/jcoupling.ts"],"names":[],"mappings":""}
|