nmr-processing 3.3.4 → 5.0.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/CHANGELOG.md +42 -0
- package/lib/assignment/get13CAssignments.d.ts +35 -0
- package/lib/assignment/get13CAssignments.js +54 -0
- package/lib/assignment/get13CAssignments.js.map +1 -0
- package/lib/assignment/get1HAssignments.d.ts +42 -0
- package/lib/assignment/get1HAssignments.js +55 -0
- package/lib/assignment/get1HAssignments.js.map +1 -0
- package/lib/assignment/utils/buildAssignments.d.ts +33 -0
- package/lib/assignment/utils/buildAssignments.js +93 -0
- package/lib/assignment/utils/buildAssignments.js.map +1 -0
- package/lib/assignment/utils/createMapPossibleAssignments.d.ts +15 -0
- package/lib/assignment/utils/createMapPossibleAssignments.js +51 -0
- package/lib/assignment/utils/createMapPossibleAssignments.js.map +1 -0
- package/lib/assignment/utils/exploreTreeRec.d.ts +22 -0
- package/lib/assignment/utils/exploreTreeRec.js +77 -0
- package/lib/assignment/utils/exploreTreeRec.js.map +1 -0
- package/lib/assignment/utils/generateID.d.ts +1 -0
- package/lib/assignment/utils/generateID.js +13 -0
- package/lib/assignment/utils/generateID.js.map +1 -0
- package/lib/assignment/utils/partialScore.d.ts +16 -0
- package/lib/assignment/utils/partialScore.js +91 -0
- package/lib/assignment/utils/partialScore.js.map +1 -0
- package/lib/index.d.ts +2 -5
- package/lib/index.js +2 -5
- package/lib/index.js.map +1 -1
- package/lib/peaks/peaksFilterImpurities.js +1 -1
- package/lib/peaks/peaksFilterImpurities.js.map +1 -1
- package/lib/peaks/peaksToRanges.js +11 -17
- package/lib/peaks/peaksToRanges.js.map +1 -1
- package/lib/peaks/util/determineRealTop.js +3 -2
- package/lib/peaks/util/determineRealTop.js.map +1 -1
- package/lib/peaks/util/jAnalyzer.js +33 -35
- package/lib/peaks/util/jAnalyzer.js.map +1 -1
- package/lib/ranges/rangesToXY.d.ts +2 -2
- package/lib/ranges/rangesToXY.js +10 -2
- package/lib/ranges/rangesToXY.js.map +1 -1
- package/lib/signals/signals2DToZ.d.ts +2 -2
- package/lib/signals/signalsToXY.d.ts +2 -2
- package/lib/signals/simulation/simulate1D.d.ts +4 -3
- package/lib/signals/simulation/simulate1D.js.map +1 -1
- package/lib/types/NMRPeak1D.d.ts +2 -9
- package/lib/types/NMRRange.d.ts +1 -0
- package/lib/types/NMRSignal1D.d.ts +1 -0
- package/lib/types/NMRSignal2D.d.ts +3 -4
- package/lib/types/NMRZone.d.ts +4 -3
- package/lib/xy/xyAutoPeaksPicking.d.ts +4 -60
- package/lib/xy/xyAutoPeaksPicking.js +3 -3
- package/lib/xy/xyAutoPeaksPicking.js.map +1 -1
- package/lib/xyz/xyzAutoPeaksPicking.js +4 -2
- package/lib/xyz/xyzAutoPeaksPicking.js.map +1 -1
- package/lib-esm/assignment/get13CAssignments.js +47 -0
- package/lib-esm/assignment/get13CAssignments.js.map +1 -0
- package/lib-esm/assignment/get1HAssignments.js +48 -0
- package/lib-esm/assignment/get1HAssignments.js.map +1 -0
- package/lib-esm/assignment/utils/buildAssignments.js +86 -0
- package/lib-esm/assignment/utils/buildAssignments.js.map +1 -0
- package/lib-esm/assignment/utils/createMapPossibleAssignments.js +47 -0
- package/lib-esm/assignment/utils/createMapPossibleAssignments.js.map +1 -0
- package/lib-esm/assignment/utils/exploreTreeRec.js +73 -0
- package/lib-esm/assignment/utils/exploreTreeRec.js.map +1 -0
- package/lib-esm/assignment/utils/generateID.js +10 -0
- package/lib-esm/assignment/utils/generateID.js.map +1 -0
- package/lib-esm/assignment/utils/partialScore.js +87 -0
- package/lib-esm/assignment/utils/partialScore.js.map +1 -0
- package/lib-esm/index.js +2 -5
- package/lib-esm/index.js.map +1 -1
- package/lib-esm/peaks/peaksFilterImpurities.js +1 -1
- package/lib-esm/peaks/peaksFilterImpurities.js.map +1 -1
- package/lib-esm/peaks/peaksToRanges.js +11 -17
- package/lib-esm/peaks/peaksToRanges.js.map +1 -1
- package/lib-esm/peaks/util/determineRealTop.js +4 -3
- package/lib-esm/peaks/util/determineRealTop.js.map +1 -1
- package/lib-esm/peaks/util/jAnalyzer.js +33 -35
- package/lib-esm/peaks/util/jAnalyzer.js.map +1 -1
- package/lib-esm/ranges/rangesToXY.js +10 -2
- package/lib-esm/ranges/rangesToXY.js.map +1 -1
- package/lib-esm/signals/simulation/simulate1D.js.map +1 -1
- package/lib-esm/xy/xyAutoPeaksPicking.js +3 -3
- package/lib-esm/xy/xyAutoPeaksPicking.js.map +1 -1
- package/lib-esm/xyz/xyzAutoPeaksPicking.js +4 -2
- package/lib-esm/xyz/xyzAutoPeaksPicking.js.map +1 -1
- package/package.json +4 -4
- package/src/assignment/get13CAssignments.ts +100 -0
- package/src/assignment/get1HAssignments.ts +116 -0
- package/src/assignment/utils/buildAssignments.ts +155 -0
- package/src/assignment/utils/createMapPossibleAssignments.ts +76 -0
- package/src/assignment/utils/exploreTreeRec.ts +138 -0
- package/src/assignment/utils/generateID.ts +11 -0
- package/src/assignment/utils/partialScore.ts +128 -0
- package/src/index.ts +3 -6
- package/src/peaks/peaksFilterImpurities.ts +1 -1
- package/src/peaks/peaksToRanges.ts +11 -17
- package/src/peaks/util/determineRealTop.ts +4 -6
- package/src/peaks/util/jAnalyzer.ts +34 -36
- package/src/ranges/rangesToXY.ts +14 -7
- package/src/signals/signals2DToZ.ts +2 -2
- package/src/signals/signalsToXY.ts +2 -2
- package/src/signals/simulation/simulate1D.ts +4 -3
- package/src/types/NMRPeak1D.ts +3 -9
- package/src/types/NMRRange.ts +1 -0
- package/src/types/NMRSignal1D.ts +1 -0
- package/src/types/NMRSignal2D.ts +4 -4
- package/src/types/NMRZone.ts +4 -3
- package/src/types/ml-spectra-processing/index.d.ts +8 -8
- package/src/types/ml-tree-set/index.d.ts +18 -0
- package/src/types/openchemlib-utils/index..d.ts +2 -2
- package/src/xy/xyAutoPeaksPicking.ts +19 -58
- package/src/xyz/xyzAutoPeaksPicking.ts +4 -2
- package/src/types/ml-gsd/index.d.ts +0 -164
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Molecule } from 'openchemlib';
|
|
2
|
+
import { addDiastereotopicMissingChirality } from 'openchemlib-utils';
|
|
3
|
+
|
|
4
|
+
import { NMRRange } from '..';
|
|
5
|
+
import {
|
|
6
|
+
predictProton,
|
|
7
|
+
PredictProtonOptions,
|
|
8
|
+
} from '../prediction/predictProton';
|
|
9
|
+
import { MakeMandatory } from '../types/MakeMandatory';
|
|
10
|
+
import type { NMRSignal1D } from '../types/NMRSignal1D';
|
|
11
|
+
|
|
12
|
+
import { buildAssignments, RestrictionByCS } from './utils/buildAssignments';
|
|
13
|
+
import generateID from './utils/generateID';
|
|
14
|
+
|
|
15
|
+
export type NMRSignal1DWithAtomsAndDiaIDs = MakeMandatory<
|
|
16
|
+
NMRSignal1D,
|
|
17
|
+
'atoms' | 'diaIDs' | 'nbAtoms'
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
function checkAtomsAndDiaIDs(
|
|
21
|
+
signals: NMRSignal1D[],
|
|
22
|
+
): asserts signals is NMRSignal1DWithAtomsAndDiaIDs[] {
|
|
23
|
+
for (const signal of signals) {
|
|
24
|
+
if (!signal.atoms) throw new Error('signal has not atoms property');
|
|
25
|
+
if (!signal.diaIDs) throw new Error('signal has not diaIDs property');
|
|
26
|
+
if (!signal.nbAtoms) throw new Error('signal has not nbAtoms property');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type NMRRangeWithIntegration = MakeMandatory<NMRRange, 'integration'>;
|
|
31
|
+
|
|
32
|
+
function checkForIntegration(
|
|
33
|
+
ranges: NMRRange[],
|
|
34
|
+
): asserts ranges is NMRRangeWithIntegration[] {
|
|
35
|
+
for (let range of ranges) {
|
|
36
|
+
if (range.integration === undefined) {
|
|
37
|
+
throw new Error('ranges has not integration property');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface Get1HAssignmentsOptions {
|
|
43
|
+
restrictionByCS?: Partial<RestrictionByCS>;
|
|
44
|
+
/**
|
|
45
|
+
* min score to accept an assignment
|
|
46
|
+
* @default 1
|
|
47
|
+
*/
|
|
48
|
+
minScore?: number;
|
|
49
|
+
/**
|
|
50
|
+
* maximal number of assignments to return
|
|
51
|
+
* @default 10
|
|
52
|
+
*/
|
|
53
|
+
maxSolutions?: number;
|
|
54
|
+
/**
|
|
55
|
+
* number of allowed unassignment signals
|
|
56
|
+
* @default 0
|
|
57
|
+
*/
|
|
58
|
+
nbAllowedUnAssigned?: number;
|
|
59
|
+
/**
|
|
60
|
+
* time limit in miliseconds to finish the assignment procedure.
|
|
61
|
+
* @default 6000
|
|
62
|
+
*/
|
|
63
|
+
timeout?: number;
|
|
64
|
+
/**
|
|
65
|
+
* prediction options
|
|
66
|
+
*/
|
|
67
|
+
predictionOptions?: PredictProtonOptions;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface Targets {
|
|
71
|
+
[key: string]: NMRRangeWithIntegration;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function get1HAssignments(
|
|
75
|
+
ranges: NMRRange[],
|
|
76
|
+
molecule: Molecule,
|
|
77
|
+
options: Get1HAssignmentsOptions = {},
|
|
78
|
+
) {
|
|
79
|
+
let {
|
|
80
|
+
restrictionByCS,
|
|
81
|
+
minScore = 1,
|
|
82
|
+
maxSolutions = 10,
|
|
83
|
+
nbAllowedUnAssigned = 0,
|
|
84
|
+
timeout = 6000,
|
|
85
|
+
predictionOptions = {},
|
|
86
|
+
} = options;
|
|
87
|
+
|
|
88
|
+
if (!molecule) {
|
|
89
|
+
throw new Error('It is needed a OCL molecule instance to assign');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
molecule.addImplicitHydrogens();
|
|
93
|
+
addDiastereotopicMissingChirality(molecule);
|
|
94
|
+
|
|
95
|
+
const { joinedSignals } = await predictProton(molecule, predictionOptions);
|
|
96
|
+
|
|
97
|
+
checkForIntegration(ranges);
|
|
98
|
+
checkAtomsAndDiaIDs(joinedSignals);
|
|
99
|
+
|
|
100
|
+
const targets: Targets = {};
|
|
101
|
+
for (const range of ranges) {
|
|
102
|
+
const { id = generateID() } = range;
|
|
103
|
+
targets[id] = JSON.parse(JSON.stringify(range));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return buildAssignments({
|
|
107
|
+
restrictionByCS,
|
|
108
|
+
timeout,
|
|
109
|
+
minScore,
|
|
110
|
+
nbAllowedUnAssigned,
|
|
111
|
+
maxSolutions,
|
|
112
|
+
targets,
|
|
113
|
+
joinedSignals,
|
|
114
|
+
useIntegrationRestriction: true,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import treeSet from 'ml-tree-set';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Targets,
|
|
5
|
+
NMRSignal1DWithAtomsAndDiaIDs,
|
|
6
|
+
} from '../get1HAssignments';
|
|
7
|
+
|
|
8
|
+
import { createMapPossibleAssignments } from './createMapPossibleAssignments';
|
|
9
|
+
import { exploreTreeRec } from './exploreTreeRec';
|
|
10
|
+
import type { SolutionAssignment } from './exploreTreeRec';
|
|
11
|
+
|
|
12
|
+
const comparator = (a: SolutionAssignment, b: SolutionAssignment) => {
|
|
13
|
+
return b.score - a.score;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export interface RestrictionByCS {
|
|
17
|
+
chemicalShiftRestriction: boolean;
|
|
18
|
+
tolerance: number;
|
|
19
|
+
useChemicalShiftScore: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface BuildAssignmentsProps {
|
|
23
|
+
restrictionByCS?: Partial<RestrictionByCS>;
|
|
24
|
+
timeout: number;
|
|
25
|
+
minScore: number;
|
|
26
|
+
useIntegrationRestriction: boolean;
|
|
27
|
+
nbAllowedUnAssigned: number;
|
|
28
|
+
maxSolutions: number;
|
|
29
|
+
targets: Targets;
|
|
30
|
+
joinedSignals: NMRSignal1DWithAtomsAndDiaIDs[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Signals1DAssignment extends NMRSignal1DWithAtomsAndDiaIDs {
|
|
34
|
+
diaIDIndex: number;
|
|
35
|
+
allHydrogens: number;
|
|
36
|
+
error?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Predictions1Dassignments {
|
|
40
|
+
[key: string]: Signals1DAssignment;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface StoreAssignments1D {
|
|
44
|
+
solutions: treeSet;
|
|
45
|
+
nSolutions: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function buildAssignments(props: BuildAssignmentsProps) {
|
|
49
|
+
const {
|
|
50
|
+
restrictionByCS = {},
|
|
51
|
+
useIntegrationRestriction,
|
|
52
|
+
timeout,
|
|
53
|
+
minScore,
|
|
54
|
+
nbAllowedUnAssigned,
|
|
55
|
+
maxSolutions,
|
|
56
|
+
targets,
|
|
57
|
+
joinedSignals,
|
|
58
|
+
} = props;
|
|
59
|
+
|
|
60
|
+
const {
|
|
61
|
+
tolerance = 1,
|
|
62
|
+
useChemicalShiftScore = false,
|
|
63
|
+
chemicalShiftRestriction = true,
|
|
64
|
+
} = restrictionByCS;
|
|
65
|
+
|
|
66
|
+
let date = new Date();
|
|
67
|
+
let timeStart = date.getTime();
|
|
68
|
+
|
|
69
|
+
let store: StoreAssignments1D = {
|
|
70
|
+
solutions: new treeSet(comparator),
|
|
71
|
+
nSolutions: 0,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let nSources = joinedSignals.length;
|
|
75
|
+
const predictions: Predictions1Dassignments = {};
|
|
76
|
+
|
|
77
|
+
for (let prediction of joinedSignals) {
|
|
78
|
+
const diaID = prediction.diaIDs[0];
|
|
79
|
+
const index = prediction.atoms[0];
|
|
80
|
+
predictions[diaID] = {
|
|
81
|
+
...prediction,
|
|
82
|
+
diaIDIndex: index,
|
|
83
|
+
allHydrogens: prediction.nbAtoms,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const possibleAssignmentMap = createMapPossibleAssignments({
|
|
88
|
+
restrictionByCS: {
|
|
89
|
+
tolerance,
|
|
90
|
+
useChemicalShiftScore,
|
|
91
|
+
chemicalShiftRestriction,
|
|
92
|
+
},
|
|
93
|
+
useIntegrationRestriction,
|
|
94
|
+
predictions,
|
|
95
|
+
targets,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const diaIDPeerPossibleAssignment = Object.keys(possibleAssignmentMap);
|
|
99
|
+
|
|
100
|
+
let partial = fillPartial(nSources);
|
|
101
|
+
|
|
102
|
+
store = {
|
|
103
|
+
solutions: new treeSet(comparator),
|
|
104
|
+
nSolutions: 0,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
exploreTreeRec(
|
|
108
|
+
{
|
|
109
|
+
nSources,
|
|
110
|
+
restrictionByCS: {
|
|
111
|
+
tolerance,
|
|
112
|
+
useChemicalShiftScore,
|
|
113
|
+
chemicalShiftRestriction,
|
|
114
|
+
},
|
|
115
|
+
timeout,
|
|
116
|
+
timeStart,
|
|
117
|
+
targets,
|
|
118
|
+
predictions,
|
|
119
|
+
maxSolutions,
|
|
120
|
+
lowerBoundScore: minScore,
|
|
121
|
+
nbAllowedUnAssigned,
|
|
122
|
+
possibleAssignmentMap,
|
|
123
|
+
diaIDPeerPossibleAssignment,
|
|
124
|
+
useIntegrationRestriction,
|
|
125
|
+
},
|
|
126
|
+
0,
|
|
127
|
+
partial,
|
|
128
|
+
store,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const assignments = [];
|
|
132
|
+
for (const solution of store.solutions.elements) {
|
|
133
|
+
const { assignment, score } = solution;
|
|
134
|
+
const currentAssignment = JSON.parse(JSON.stringify(targets));
|
|
135
|
+
for (let i = 0; i < assignment.length; i++) {
|
|
136
|
+
let range = currentAssignment[assignment[i]];
|
|
137
|
+
if (!range.diaIDs) range.diaIDs = [];
|
|
138
|
+
if (assignment[i]) range.diaIDs.push(diaIDPeerPossibleAssignment[i]);
|
|
139
|
+
}
|
|
140
|
+
assignments.push({
|
|
141
|
+
score,
|
|
142
|
+
assignment: Object.values(currentAssignment),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return assignments;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function fillPartial(nSources: number, value = null) {
|
|
150
|
+
const partial = new Array(nSources);
|
|
151
|
+
for (let i = 0; i < nSources; i++) {
|
|
152
|
+
partial[i] = value;
|
|
153
|
+
}
|
|
154
|
+
return partial;
|
|
155
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { MakeMandatory } from '../../types/MakeMandatory';
|
|
2
|
+
import type { Targets } from '../get1HAssignments';
|
|
3
|
+
|
|
4
|
+
import type { RestrictionByCS, Predictions1Dassignments } from './buildAssignments';
|
|
5
|
+
|
|
6
|
+
type RestrictionByCSMandatory = MakeMandatory<
|
|
7
|
+
RestrictionByCS,
|
|
8
|
+
'chemicalShiftRestriction' | 'tolerance' | 'useChemicalShiftScore'
|
|
9
|
+
>;
|
|
10
|
+
|
|
11
|
+
interface CreateMapPossibleAssignments {
|
|
12
|
+
predictions: Predictions1Dassignments;
|
|
13
|
+
restrictionByCS: RestrictionByCSMandatory;
|
|
14
|
+
targets: Targets;
|
|
15
|
+
useIntegrationRestriction: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PossibleAssignmentMap {
|
|
19
|
+
[key: string]: string[];
|
|
20
|
+
}
|
|
21
|
+
export function createMapPossibleAssignments(
|
|
22
|
+
props: CreateMapPossibleAssignments,
|
|
23
|
+
) {
|
|
24
|
+
const { restrictionByCS, predictions, targets, useIntegrationRestriction } =
|
|
25
|
+
props;
|
|
26
|
+
|
|
27
|
+
const { tolerance: toleranceCS, chemicalShiftRestriction } = restrictionByCS;
|
|
28
|
+
|
|
29
|
+
let errorAbs = Math.abs(toleranceCS);
|
|
30
|
+
const expandMap: PossibleAssignmentMap = {};
|
|
31
|
+
for (const diaID in predictions) {
|
|
32
|
+
let prediction = predictions[diaID];
|
|
33
|
+
if (prediction.error) prediction.error = Math.abs(prediction.error);
|
|
34
|
+
expandMap[diaID] = [];
|
|
35
|
+
|
|
36
|
+
if (targets) {
|
|
37
|
+
for (const targetID in targets) {
|
|
38
|
+
let target = targets[targetID];
|
|
39
|
+
const { nbAtoms } = prediction;
|
|
40
|
+
const { integration } = target;
|
|
41
|
+
|
|
42
|
+
const couldBeAssigned = useIntegrationRestriction
|
|
43
|
+
? integration > 0
|
|
44
|
+
? nbAtoms - integration < 1
|
|
45
|
+
: true
|
|
46
|
+
: true;
|
|
47
|
+
|
|
48
|
+
if (couldBeAssigned) {
|
|
49
|
+
if (
|
|
50
|
+
!chemicalShiftRestriction ||
|
|
51
|
+
typeof prediction.delta === 'undefined'
|
|
52
|
+
) {
|
|
53
|
+
// Chemical shift is not a restriction
|
|
54
|
+
expandMap[diaID].push(targetID);
|
|
55
|
+
} else {
|
|
56
|
+
let error = errorAbs;
|
|
57
|
+
if (prediction.error) {
|
|
58
|
+
error = Math.max(error, prediction.error);
|
|
59
|
+
}
|
|
60
|
+
const delta =
|
|
61
|
+
target.signals && target.signals.length > 0
|
|
62
|
+
? target.signals[0].delta
|
|
63
|
+
: (target.to + target.from) / 2;
|
|
64
|
+
|
|
65
|
+
let distAfterLimit = Math.abs(prediction.delta - delta - errorAbs);
|
|
66
|
+
if (distAfterLimit < 4 * errorAbs) {
|
|
67
|
+
expandMap[diaID].push(targetID);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
expandMap[diaID].push('*');
|
|
74
|
+
}
|
|
75
|
+
return expandMap;
|
|
76
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { Targets } from '../get1HAssignments';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
RestrictionByCS,
|
|
5
|
+
StoreAssignments1D,
|
|
6
|
+
Predictions1Dassignments,
|
|
7
|
+
} from './buildAssignments';
|
|
8
|
+
import type { PossibleAssignmentMap } from './createMapPossibleAssignments';
|
|
9
|
+
import { partialScore } from './partialScore';
|
|
10
|
+
|
|
11
|
+
export interface ExploreTreeRecProps {
|
|
12
|
+
nSources: number;
|
|
13
|
+
restrictionByCS: RestrictionByCS;
|
|
14
|
+
timeout: number;
|
|
15
|
+
timeStart: number;
|
|
16
|
+
maxSolutions: number;
|
|
17
|
+
targets: Targets;
|
|
18
|
+
useIntegrationRestriction: boolean;
|
|
19
|
+
predictions: Predictions1Dassignments;
|
|
20
|
+
lowerBoundScore: number;
|
|
21
|
+
nbAllowedUnAssigned: number;
|
|
22
|
+
possibleAssignmentMap: PossibleAssignmentMap;
|
|
23
|
+
diaIDPeerPossibleAssignment: string[];
|
|
24
|
+
}
|
|
25
|
+
export function exploreTreeRec(
|
|
26
|
+
props: ExploreTreeRecProps,
|
|
27
|
+
currentIndex: number,
|
|
28
|
+
partial: Array<string | null>,
|
|
29
|
+
store: StoreAssignments1D,
|
|
30
|
+
) {
|
|
31
|
+
const {
|
|
32
|
+
nSources,
|
|
33
|
+
restrictionByCS,
|
|
34
|
+
timeout,
|
|
35
|
+
timeStart,
|
|
36
|
+
maxSolutions,
|
|
37
|
+
targets,
|
|
38
|
+
predictions,
|
|
39
|
+
lowerBoundScore,
|
|
40
|
+
nbAllowedUnAssigned,
|
|
41
|
+
possibleAssignmentMap,
|
|
42
|
+
useIntegrationRestriction,
|
|
43
|
+
diaIDPeerPossibleAssignment,
|
|
44
|
+
} = props;
|
|
45
|
+
|
|
46
|
+
const currentDate = new Date();
|
|
47
|
+
if (currentDate.getTime() - timeStart > timeout) {
|
|
48
|
+
new Error('timeout expired');
|
|
49
|
+
return store;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const diaID = diaIDPeerPossibleAssignment[currentIndex];
|
|
53
|
+
const possibleAssignments = possibleAssignmentMap[diaID];
|
|
54
|
+
for (let targetID of possibleAssignments) {
|
|
55
|
+
partial[currentIndex] = targetID;
|
|
56
|
+
let score = partialScore(partial, {
|
|
57
|
+
useIntegrationRestriction,
|
|
58
|
+
diaIDPeerPossibleAssignment,
|
|
59
|
+
nbAllowedUnAssigned,
|
|
60
|
+
restrictionByCS,
|
|
61
|
+
predictions,
|
|
62
|
+
targets,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (score === 0) {
|
|
66
|
+
if (targetID === '*') {
|
|
67
|
+
partial[currentIndex] = null;
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (currentIndex === nSources - 1 && score >= lowerBoundScore) {
|
|
73
|
+
addSolution(store, { predictions, partial, score, maxSolutions });
|
|
74
|
+
} else if (currentIndex < nSources - 1) {
|
|
75
|
+
exploreTreeRec(
|
|
76
|
+
{
|
|
77
|
+
nSources,
|
|
78
|
+
restrictionByCS,
|
|
79
|
+
timeout,
|
|
80
|
+
timeStart,
|
|
81
|
+
maxSolutions,
|
|
82
|
+
targets,
|
|
83
|
+
predictions,
|
|
84
|
+
lowerBoundScore,
|
|
85
|
+
nbAllowedUnAssigned,
|
|
86
|
+
possibleAssignmentMap,
|
|
87
|
+
useIntegrationRestriction,
|
|
88
|
+
diaIDPeerPossibleAssignment,
|
|
89
|
+
},
|
|
90
|
+
currentIndex + 1,
|
|
91
|
+
JSON.parse(JSON.stringify(partial)),
|
|
92
|
+
store,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface SolutionAssignment {
|
|
99
|
+
assignment: string[];
|
|
100
|
+
score: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface AddSolutionProps {
|
|
104
|
+
score: number;
|
|
105
|
+
maxSolutions: number;
|
|
106
|
+
partial: Array<string | null>;
|
|
107
|
+
predictions: Predictions1Dassignments;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function addSolution(store: StoreAssignments1D, props: AddSolutionProps) {
|
|
111
|
+
let { score, maxSolutions, partial, predictions } = props;
|
|
112
|
+
score /= doubleAssignmentPenalty(partial, predictions);
|
|
113
|
+
store.nSolutions++;
|
|
114
|
+
let solution: SolutionAssignment = {
|
|
115
|
+
assignment: JSON.parse(JSON.stringify(partial)),
|
|
116
|
+
score: score,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
if (store.nSolutions >= maxSolutions) {
|
|
120
|
+
if (solution.score > store.solutions.last().score) {
|
|
121
|
+
store.solutions.pollLast();
|
|
122
|
+
store.solutions.add(solution);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
store.solutions.add(solution);
|
|
126
|
+
store.nSolutions++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function doubleAssignmentPenalty(
|
|
131
|
+
partial: Array<string | null>,
|
|
132
|
+
predictions: Predictions1Dassignments,
|
|
133
|
+
) {
|
|
134
|
+
const nbSources = Object.keys(predictions).length;
|
|
135
|
+
let assignments = new Set(partial);
|
|
136
|
+
let nbDoubleAssignment = nbSources - assignments.size;
|
|
137
|
+
return nbDoubleAssignment > 0 ? 2 * nbDoubleAssignment : 1;
|
|
138
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
2
|
+
|
|
3
|
+
const LENGTH = 8;
|
|
4
|
+
|
|
5
|
+
export default function generateID() {
|
|
6
|
+
let id = '';
|
|
7
|
+
for (let i = 0; i < LENGTH; i++) {
|
|
8
|
+
id += BASE62.charAt(Math.floor(Math.random() * 62));
|
|
9
|
+
}
|
|
10
|
+
return id;
|
|
11
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Targets } from '../get1HAssignments';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
Predictions1Dassignments,
|
|
5
|
+
RestrictionByCS,
|
|
6
|
+
} from './buildAssignments';
|
|
7
|
+
|
|
8
|
+
interface PartialScoreOptions {
|
|
9
|
+
restrictionByCS: RestrictionByCS;
|
|
10
|
+
/**
|
|
11
|
+
* number of allowed unassignment signals
|
|
12
|
+
* @default 0
|
|
13
|
+
*/
|
|
14
|
+
useIntegrationRestriction: boolean;
|
|
15
|
+
nbAllowedUnAssigned: number;
|
|
16
|
+
diaIDPeerPossibleAssignment: string[];
|
|
17
|
+
predictions: Predictions1Dassignments;
|
|
18
|
+
targets: Targets;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function partialScore(
|
|
22
|
+
partial: Array<string | null>,
|
|
23
|
+
options: PartialScoreOptions,
|
|
24
|
+
) {
|
|
25
|
+
const {
|
|
26
|
+
useIntegrationRestriction,
|
|
27
|
+
diaIDPeerPossibleAssignment,
|
|
28
|
+
nbAllowedUnAssigned,
|
|
29
|
+
restrictionByCS,
|
|
30
|
+
predictions,
|
|
31
|
+
targets,
|
|
32
|
+
} = options;
|
|
33
|
+
const { useChemicalShiftScore } = restrictionByCS;
|
|
34
|
+
let countStars = 0;
|
|
35
|
+
let totalPartial = partial.length;
|
|
36
|
+
let partialInverse: {
|
|
37
|
+
[key: string]: string[];
|
|
38
|
+
} = {};
|
|
39
|
+
let activeDomainOnPrediction: number[] = [];
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < partial.length; i++) {
|
|
42
|
+
const targetID = partial[i];
|
|
43
|
+
if (targetID && targetID !== '*') {
|
|
44
|
+
activeDomainOnPrediction.push(i);
|
|
45
|
+
if (!partialInverse[targetID]) {
|
|
46
|
+
partialInverse[targetID] = [];
|
|
47
|
+
}
|
|
48
|
+
partialInverse[targetID].push(diaIDPeerPossibleAssignment[i]);
|
|
49
|
+
}
|
|
50
|
+
if (targetID === '*') countStars++;
|
|
51
|
+
}
|
|
52
|
+
if (countStars > nbAllowedUnAssigned) return 0;
|
|
53
|
+
|
|
54
|
+
const activeDomainOnTarget = Object.keys(partialInverse);
|
|
55
|
+
|
|
56
|
+
if (activeDomainOnTarget.length === 0) {
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (useIntegrationRestriction) {
|
|
61
|
+
for (let targetID of activeDomainOnTarget) {
|
|
62
|
+
let targetToSource = partialInverse[targetID];
|
|
63
|
+
let total = 0;
|
|
64
|
+
for (const diaID of targetToSource) {
|
|
65
|
+
const prediction = predictions[diaID];
|
|
66
|
+
total += prediction.allHydrogens;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { integration } = targets[targetID];
|
|
70
|
+
if (total - integration >= 0.5) {
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//chemical shift score
|
|
77
|
+
let chemicalShiftScore = useChemicalShiftScore
|
|
78
|
+
? chemicalShiftScoring(partial, options)
|
|
79
|
+
: 1;
|
|
80
|
+
|
|
81
|
+
const penaltyByStarts = countStars / totalPartial;
|
|
82
|
+
|
|
83
|
+
return chemicalShiftScore - penaltyByStarts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function chemicalShiftScoring(
|
|
87
|
+
partial: Array<string | null>,
|
|
88
|
+
options: PartialScoreOptions,
|
|
89
|
+
) {
|
|
90
|
+
const { tolerance } = options.restrictionByCS;
|
|
91
|
+
const { diaIDPeerPossibleAssignment, predictions, targets } = options;
|
|
92
|
+
|
|
93
|
+
let chemicalShiftScore = 0;
|
|
94
|
+
let count = 0;
|
|
95
|
+
for (let index = 0; index < partial.length; index++) {
|
|
96
|
+
const targetID = partial[index];
|
|
97
|
+
if (targetID && targetID !== '*') {
|
|
98
|
+
count++;
|
|
99
|
+
let diaID = diaIDPeerPossibleAssignment[index];
|
|
100
|
+
let source = predictions[diaID];
|
|
101
|
+
let target = targets[targetID];
|
|
102
|
+
let error = tolerance;
|
|
103
|
+
if (source.error) {
|
|
104
|
+
error = Math.max(source.error, tolerance);
|
|
105
|
+
}
|
|
106
|
+
if (typeof source.delta === 'undefined') {
|
|
107
|
+
// Chemical shift is not a restriction
|
|
108
|
+
chemicalShiftScore += 1;
|
|
109
|
+
} else {
|
|
110
|
+
const delta =
|
|
111
|
+
target.signals && target.signals.length > 0
|
|
112
|
+
? target.signals[0].delta
|
|
113
|
+
: (target.to + target.from) / 2;
|
|
114
|
+
let diff = Math.abs(source.delta - delta);
|
|
115
|
+
if (diff < error) {
|
|
116
|
+
chemicalShiftScore += 1;
|
|
117
|
+
} else {
|
|
118
|
+
diff = Math.abs(diff - error);
|
|
119
|
+
chemicalShiftScore += (-0.25 / error) * diff + 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (count > 0) {
|
|
125
|
+
chemicalShiftScore /= count;
|
|
126
|
+
}
|
|
127
|
+
return chemicalShiftScore;
|
|
128
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} DataXY
|
|
3
|
-
* @property {Array<Number>} x Array of x values
|
|
4
|
-
* @property {Array<Number>} y Array of y values
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
export * from './constants/gyromagneticRatio';
|
|
8
2
|
export * from './constants/impurities';
|
|
9
3
|
export * from './constants/couplingPatterns';
|
|
@@ -17,6 +11,9 @@ export * from './prediction/predictHSQC';
|
|
|
17
11
|
export * from './prediction/predictHMBC';
|
|
18
12
|
export * from './prediction/predictAll';
|
|
19
13
|
|
|
14
|
+
export * from './assignment/get1HAssignments';
|
|
15
|
+
export * from './assignment/get13CAssignments';
|
|
16
|
+
|
|
20
17
|
export * from './ranges/rangesToACS';
|
|
21
18
|
export * from './ranges/rangesToXY';
|
|
22
19
|
|
|
@@ -66,7 +66,7 @@ function checkImpurity(
|
|
|
66
66
|
while (i--) {
|
|
67
67
|
j = peakList.length;
|
|
68
68
|
while (j--) {
|
|
69
|
-
tolerance = error + peakList[j].
|
|
69
|
+
tolerance = error + peakList[j].width;
|
|
70
70
|
difference = Math.abs(impurity[i].shift - peakList[j].x);
|
|
71
71
|
if (difference < tolerance) {
|
|
72
72
|
// && (impurity[i].multiplicity === '' || (impurity[i].multiplicity.indexOf(peakList[j].multiplicity)) { // some impurities has multiplicities like 'bs' but at presents it is unsupported
|