nmr-processing 9.1.0 → 9.2.0-pre.1668170175

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.
Files changed (133) hide show
  1. package/lib/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
  2. package/lib/index.d.ts +4 -2
  3. package/lib/index.js +2 -0
  4. package/lib/index.js.map +1 -1
  5. package/lib/prediction/{prediction1D.d.ts → Prediction1D.d.ts} +0 -0
  6. package/lib/prediction/{prediction1D.js → Prediction1D.js} +1 -1
  7. package/lib/prediction/Prediction1D.js.map +1 -0
  8. package/lib/prediction/predictAll.d.ts +1 -1
  9. package/lib/prediction/predictAll.js.map +1 -1
  10. package/lib/prediction/predictAllSpectra.js +2 -2
  11. package/lib/prediction/predictAllSpectra.js.map +1 -1
  12. package/lib/prediction/predictCOSY.d.ts +1 -1
  13. package/lib/prediction/predictCOSY.js.map +1 -1
  14. package/lib/prediction/predictCarbon.d.ts +1 -1
  15. package/lib/prediction/predictHMBC.d.ts +1 -1
  16. package/lib/prediction/predictHMBC.js.map +1 -1
  17. package/lib/prediction/predictHSQC.d.ts +1 -1
  18. package/lib/prediction/predictHSQC.js.map +1 -1
  19. package/lib/prediction/predictProton.d.ts +1 -1
  20. package/lib/prediction/utils/fetchPrediction.d.ts +1 -1
  21. package/lib/prediction/utils/getPredictions.d.ts +1 -1
  22. package/lib/prediction/utils/predict2D.d.ts +1 -1
  23. package/lib/prediction/utils/predict2D.js +1 -1
  24. package/lib/prediction/utils/predict2D.js.map +1 -1
  25. package/lib/signals/{jcoupling.d.ts → Jcoupling.d.ts} +0 -0
  26. package/lib/signals/{jcoupling.js → Jcoupling.js} +1 -1
  27. package/lib/signals/Jcoupling.js.map +1 -0
  28. package/lib/signals/NMRSignal1D.d.ts +1 -1
  29. package/lib/signals/addDummySignals.js +12 -7
  30. package/lib/signals/addDummySignals.js.map +1 -1
  31. package/lib/signals/optimization/defaultParameters.d.ts +1 -0
  32. package/lib/signals/optimization/defaultParameters.js +36 -0
  33. package/lib/signals/optimization/defaultParameters.js.map +1 -0
  34. package/lib/signals/optimization/directOptimization.d.ts +20 -0
  35. package/lib/signals/optimization/directOptimization.js +32 -0
  36. package/lib/signals/optimization/directOptimization.js.map +1 -0
  37. package/lib/signals/optimization/getInternalSignals.d.ts +54 -0
  38. package/lib/signals/optimization/getInternalSignals.js +150 -0
  39. package/lib/signals/optimization/getInternalSignals.js.map +1 -0
  40. package/lib/signals/optimization/getSumOfShapes.d.ts +4 -0
  41. package/lib/signals/optimization/getSumOfShapes.js +46 -0
  42. package/lib/signals/optimization/getSumOfShapes.js.map +1 -0
  43. package/lib/signals/optimization/signalsToPointXY.d.ts +9 -0
  44. package/lib/signals/optimization/signalsToPointXY.js +24 -0
  45. package/lib/signals/optimization/signalsToPointXY.js.map +1 -0
  46. package/lib/signals/optimizeSignals.d.ts +40 -0
  47. package/lib/signals/optimizeSignals.js +108 -0
  48. package/lib/signals/optimizeSignals.js.map +1 -0
  49. package/lib/signals/signalsJoin.d.ts +1 -1
  50. package/lib/signals/signalsToFID.d.ts +13 -0
  51. package/lib/signals/signalsToFID.js +56 -0
  52. package/lib/signals/signalsToFID.js.map +1 -0
  53. package/lib/signals/simulation/simulateXYPeaks.js +1 -1
  54. package/lib/signals/simulation/simulateXYPeaks.js.map +1 -1
  55. package/lib/xy/xyAutoPeaksPicking.d.ts +2 -2
  56. package/lib/xy/xyAutoPeaksPicking.js +2 -2
  57. package/lib/xy/xyAutoPeaksPicking.js.map +1 -1
  58. package/lib/xy/xyPeaksOptimization.js +2 -1
  59. package/lib/xy/xyPeaksOptimization.js.map +1 -1
  60. package/lib-esm/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
  61. package/lib-esm/index.js +2 -0
  62. package/lib-esm/index.js.map +1 -1
  63. package/lib-esm/prediction/Prediction1D.js +2 -0
  64. package/lib-esm/prediction/Prediction1D.js.map +1 -0
  65. package/lib-esm/prediction/predictAll.js.map +1 -1
  66. package/lib-esm/prediction/predictAllSpectra.js +2 -2
  67. package/lib-esm/prediction/predictAllSpectra.js.map +1 -1
  68. package/lib-esm/prediction/predictCOSY.js.map +1 -1
  69. package/lib-esm/prediction/predictHMBC.js.map +1 -1
  70. package/lib-esm/prediction/predictHSQC.js.map +1 -1
  71. package/lib-esm/prediction/utils/predict2D.js +1 -1
  72. package/lib-esm/prediction/utils/predict2D.js.map +1 -1
  73. package/lib-esm/signals/Jcoupling.js +2 -0
  74. package/lib-esm/signals/Jcoupling.js.map +1 -0
  75. package/lib-esm/signals/addDummySignals.js +12 -7
  76. package/lib-esm/signals/addDummySignals.js.map +1 -1
  77. package/lib-esm/signals/optimization/defaultParameters.js +33 -0
  78. package/lib-esm/signals/optimization/defaultParameters.js.map +1 -0
  79. package/lib-esm/signals/optimization/directOptimization.js +25 -0
  80. package/lib-esm/signals/optimization/directOptimization.js.map +1 -0
  81. package/lib-esm/signals/optimization/getInternalSignals.js +146 -0
  82. package/lib-esm/signals/optimization/getInternalSignals.js.map +1 -0
  83. package/lib-esm/signals/optimization/getSumOfShapes.js +42 -0
  84. package/lib-esm/signals/optimization/getSumOfShapes.js.map +1 -0
  85. package/lib-esm/signals/optimization/signalsToPointXY.js +20 -0
  86. package/lib-esm/signals/optimization/signalsToPointXY.js.map +1 -0
  87. package/lib-esm/signals/optimizeSignals.js +104 -0
  88. package/lib-esm/signals/optimizeSignals.js.map +1 -0
  89. package/lib-esm/signals/signalsToFID.js +52 -0
  90. package/lib-esm/signals/signalsToFID.js.map +1 -0
  91. package/lib-esm/signals/simulation/simulateXYPeaks.js +1 -1
  92. package/lib-esm/signals/simulation/simulateXYPeaks.js.map +1 -1
  93. package/lib-esm/xy/xyAutoPeaksPicking.js +3 -3
  94. package/lib-esm/xy/xyAutoPeaksPicking.js.map +1 -1
  95. package/lib-esm/xy/xyPeaksOptimization.js +2 -1
  96. package/lib-esm/xy/xyPeaksOptimization.js.map +1 -1
  97. package/package.json +3 -2
  98. package/src/assignment/utils/getAssignment/buildAssignments.ts +1 -1
  99. package/src/index.ts +4 -2
  100. package/src/prediction/{prediction1D.ts → Prediction1D.ts} +0 -0
  101. package/src/prediction/predictAll.ts +1 -1
  102. package/src/prediction/predictAllSpectra.ts +2 -2
  103. package/src/prediction/predictCOSY.ts +1 -1
  104. package/src/prediction/predictCarbon.ts +1 -1
  105. package/src/prediction/predictHMBC.ts +1 -1
  106. package/src/prediction/predictHSQC.ts +1 -1
  107. package/src/prediction/predictProton.ts +1 -1
  108. package/src/prediction/utils/fetchPrediction.ts +1 -1
  109. package/src/prediction/utils/getPredictions.ts +1 -1
  110. package/src/prediction/utils/predict2D.ts +2 -2
  111. package/src/signal/signalJoinCouplings.ts +1 -1
  112. package/src/signals/{jcoupling.ts → Jcoupling.ts} +0 -0
  113. package/src/signals/NMRSignal1D.ts +1 -1
  114. package/src/signals/addDummySignals.ts +17 -7
  115. package/src/signals/optimization/defaultParameters.ts +45 -0
  116. package/src/signals/optimization/directOptimization.ts +49 -0
  117. package/src/signals/optimization/getInternalSignals.ts +287 -0
  118. package/src/signals/optimization/getSumOfShapes.ts +54 -0
  119. package/src/signals/optimization/signalsToPointXY.ts +37 -0
  120. package/src/signals/optimizeSignals.ts +173 -0
  121. package/src/signals/signalsJoin.ts +1 -1
  122. package/src/signals/signalsToFID.ts +84 -0
  123. package/src/signals/signalsToXY.ts +1 -1
  124. package/src/signals/simulation/signalsToSpinSystem.ts +1 -1
  125. package/src/signals/simulation/simulateXYPeaks.ts +1 -2
  126. package/src/xy/xyAutoPeaksPicking.ts +10 -6
  127. package/src/xy/xyPeaksOptimization.ts +3 -1
  128. package/lib/prediction/prediction1D.js.map +0 -1
  129. package/lib/signals/jcoupling.js.map +0 -1
  130. package/lib-esm/prediction/prediction1D.js +0 -2
  131. package/lib-esm/prediction/prediction1D.js.map +0 -1
  132. package/lib-esm/signals/jcoupling.js +0 -2
  133. package/lib-esm/signals/jcoupling.js.map +0 -1
@@ -0,0 +1,49 @@
1
+ import { DataXY } from 'cheminfo-types';
2
+ import direct from 'ml-direct';
3
+
4
+ import { SumOfShapes } from './getSumOfShapes';
5
+
6
+ interface DirectOptions {
7
+ iterations?: number;
8
+ epsilon?: number;
9
+ tolerance?: number;
10
+ tolerance2?: number;
11
+ initialState?: any;
12
+ }
13
+
14
+ interface DirectOptimizationOptions extends DirectOptions {
15
+ directOptions?: DirectOptions;
16
+ minValues: number[];
17
+ maxValues: number[];
18
+ }
19
+
20
+ export function directOptimization(
21
+ data: DataXY,
22
+ sumOfShapes: SumOfShapes,
23
+ options: DirectOptimizationOptions,
24
+ ) {
25
+ const { minValues, maxValues, directOptions = {} } = options;
26
+ const objectiveFunction = getObjectiveFunction(data, sumOfShapes);
27
+ const result = direct(objectiveFunction, minValues, maxValues, directOptions);
28
+
29
+ const { optima } = result;
30
+
31
+ return {
32
+ error: result.minFunctionValue,
33
+ iterations: result.iterations,
34
+ parameterValues: optima[0],
35
+ };
36
+ }
37
+
38
+ function getObjectiveFunction(data: DataXY, sumOfShapes: SumOfShapes) {
39
+ const { x, y } = data;
40
+ const nbPoints = x.length;
41
+ return (parameters: number[]) => {
42
+ const fct = sumOfShapes(parameters);
43
+ let error = 0;
44
+ for (let i = 0; i < nbPoints; i++) {
45
+ error += Math.pow(y[i] - fct(x[i]), 2);
46
+ }
47
+ return error;
48
+ };
49
+ }
@@ -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
  /**
@@ -0,0 +1,84 @@
1
+ import { xSequentialFill } from 'ml-spectra-processing';
2
+
3
+ import { apodization } from '../apodization/apodization';
4
+
5
+ import { NMRSignal1D } from './NMRSignal1D';
6
+ import { signalsToSpinSystem } from './simulation/signalsToSpinSystem';
7
+ import { simulateXYPeaks } from './simulation/simulateXYPeaks';
8
+ import { splitSpinSystem } from './simulation/splitSpinSystem';
9
+
10
+ export interface SignalsToFIDOptions {
11
+ lb?: number;
12
+ from?: number;
13
+ to?: number;
14
+ nbPoints?: number;
15
+ frequency?: number;
16
+ maxClusterSize?: number;
17
+ }
18
+
19
+ const twoPi = Math.PI * 2;
20
+
21
+ export function signalsToFID(
22
+ signals: NMRSignal1D[],
23
+ options: SignalsToFIDOptions,
24
+ ) {
25
+ const {
26
+ lb = 1,
27
+ from = 0,
28
+ to = 10,
29
+ nbPoints = 1024,
30
+ frequency = 400,
31
+ maxClusterSize = 8,
32
+ } = options;
33
+
34
+ let spinSystem = signalsToSpinSystem(signals);
35
+
36
+ spinSystem.clusters = splitSpinSystem(spinSystem, {
37
+ frequency,
38
+ maxClusterSize,
39
+ });
40
+
41
+ const peaks = simulateXYPeaks(spinSystem, { frequency });
42
+ const re = new Float64Array(nbPoints);
43
+ const im = new Float64Array(nbPoints);
44
+
45
+ const sw = Math.abs(to - from);
46
+ const adquisitionTime = nbPoints / (2 * sw * frequency);
47
+ const time = xSequentialFill({
48
+ size: nbPoints,
49
+ from: 0,
50
+ to: adquisitionTime,
51
+ });
52
+
53
+ for (const peak of peaks) {
54
+ const { x, y } = peak;
55
+ const cs = x * frequency * twoPi;
56
+ for (let i = 0; i < nbPoints; i++) {
57
+ re[i] += y * Math.cos(cs * time[i]);
58
+ im[i] += y * Math.sin(cs * time[i]);
59
+ }
60
+ }
61
+
62
+ const { windowData, ...result } = apodization(
63
+ { re, im },
64
+ {
65
+ pointsToShift: 0,
66
+ compose: {
67
+ length: nbPoints,
68
+ shapes: [
69
+ {
70
+ start: 0,
71
+ shape: {
72
+ kind: 'exponential',
73
+ options: {
74
+ dw: adquisitionTime / (nbPoints - 1),
75
+ lb,
76
+ },
77
+ },
78
+ },
79
+ ],
80
+ },
81
+ },
82
+ );
83
+ return result;
84
+ }
@@ -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';