@aleph-ai/tinyaleph 1.2.0 → 1.3.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/README.md +187 -2
- package/backends/bioinformatics/binding.js +503 -0
- package/backends/bioinformatics/dna-computing.js +664 -0
- package/backends/bioinformatics/encoding.js +339 -0
- package/backends/bioinformatics/folding.js +454 -0
- package/backends/bioinformatics/genetic-code.js +269 -0
- package/backends/bioinformatics/index.js +522 -0
- package/backends/bioinformatics/transcription.js +221 -0
- package/backends/bioinformatics/translation.js +264 -0
- package/backends/index.js +25 -1
- package/core/compound.js +532 -0
- package/core/hilbert.js +454 -1
- package/core/index.js +106 -12
- package/core/inference.js +605 -0
- package/core/resonance.js +245 -616
- package/core/symbols/archetypes.js +478 -0
- package/core/symbols/base.js +302 -0
- package/core/symbols/elements.js +487 -0
- package/core/symbols/hieroglyphs.js +303 -0
- package/core/symbols/iching.js +471 -0
- package/core/symbols/index.js +77 -0
- package/core/symbols/tarot.js +211 -0
- package/core/symbols.js +22 -0
- package/docs/design/BIOINFORMATICS_BACKEND_DESIGN.md +493 -0
- package/docs/guide/06-symbolic-ai.md +370 -0
- package/docs/guide/README.md +2 -1
- package/docs/reference/05-symbolic-ai.md +570 -0
- package/docs/reference/06-bioinformatics.md +546 -0
- package/docs/reference/README.md +32 -2
- package/docs/theory/11-prgraph-memory.md +559 -0
- package/docs/theory/12-resonant-attention.md +661 -0
- package/modular.js +33 -1
- package/package.json +1 -1
- package/physics/index.js +16 -0
- package/physics/kuramoto-coupled-ladder.js +603 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protein Folding via Kuramoto Oscillator Dynamics
|
|
3
|
+
*
|
|
4
|
+
* Models protein folding as entropy minimization through coupled oscillator synchronization.
|
|
5
|
+
* Each amino acid residue is an oscillator with:
|
|
6
|
+
* - Natural frequency derived from its prime value
|
|
7
|
+
* - Coupling strength from contact propensity (hydrophobic, electrostatic, resonance)
|
|
8
|
+
*
|
|
9
|
+
* Folded state = synchronized oscillators = low entropy configuration
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { AMINO_ACID_PRIMES, AMINO_ACID_PROPERTIES, getChargeFromPrime, getHydrophobicityFromPrime } = require('./encoding');
|
|
13
|
+
|
|
14
|
+
// Import physics modules - with fallback for standalone testing
|
|
15
|
+
let KuramotoModel, NetworkKuramoto, AdaptiveKuramoto, shannonEntropy, primeToFrequency, calculateResonance;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const physics = require('../../physics');
|
|
19
|
+
const core = require('../../core');
|
|
20
|
+
|
|
21
|
+
KuramotoModel = physics.KuramotoModel;
|
|
22
|
+
NetworkKuramoto = physics.NetworkKuramoto || physics.KuramotoModel;
|
|
23
|
+
AdaptiveKuramoto = physics.AdaptiveKuramoto || physics.KuramotoModel;
|
|
24
|
+
shannonEntropy = physics.shannonEntropy;
|
|
25
|
+
primeToFrequency = core.primeToFrequency;
|
|
26
|
+
calculateResonance = core.calculateResonance;
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// Fallback implementations for standalone testing
|
|
29
|
+
primeToFrequency = (p, base = 1, logScale = 10) => base + Math.log(p) / logScale;
|
|
30
|
+
calculateResonance = (p1, p2) => {
|
|
31
|
+
const ratio = Math.max(p1, p2) / Math.min(p1, p2);
|
|
32
|
+
const PHI = 1.618033988749895;
|
|
33
|
+
return 1 / (1 + Math.abs(ratio - PHI));
|
|
34
|
+
};
|
|
35
|
+
shannonEntropy = (probs) => {
|
|
36
|
+
let H = 0;
|
|
37
|
+
for (const p of probs) {
|
|
38
|
+
if (p > 1e-10) H -= p * Math.log2(p);
|
|
39
|
+
}
|
|
40
|
+
return H;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* FoldingTransform
|
|
46
|
+
*
|
|
47
|
+
* Protein folding modeled as entropy minimization via Kuramoto synchronization.
|
|
48
|
+
*/
|
|
49
|
+
class FoldingTransform {
|
|
50
|
+
constructor(options = {}) {
|
|
51
|
+
this.options = {
|
|
52
|
+
coupling: options.coupling || 0.3,
|
|
53
|
+
temperature: options.temperature || 1.0,
|
|
54
|
+
maxSteps: options.maxSteps || 1000,
|
|
55
|
+
dt: options.dt || 0.01,
|
|
56
|
+
convergenceThreshold: options.convergenceThreshold || 0.95,
|
|
57
|
+
minSequenceSeparation: options.minSequenceSeparation || 4,
|
|
58
|
+
hydrophobicWeight: options.hydrophobicWeight || 0.4,
|
|
59
|
+
electrostaticWeight: options.electrostaticWeight || 0.3,
|
|
60
|
+
resonanceWeight: options.resonanceWeight || 0.3,
|
|
61
|
+
...options
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Fold protein using Kuramoto oscillator dynamics
|
|
67
|
+
* @param {number[]} proteinPrimes - Protein sequence as amino acid primes
|
|
68
|
+
* @returns {object} Folding result with structure information
|
|
69
|
+
*/
|
|
70
|
+
fold(proteinPrimes) {
|
|
71
|
+
const n = proteinPrimes.length;
|
|
72
|
+
|
|
73
|
+
if (n < 4) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
error: 'Protein too short for folding simulation',
|
|
77
|
+
length: n
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Initialize oscillators with frequencies from primes
|
|
82
|
+
const frequencies = proteinPrimes.map(p => primeToFrequency(p, 1, 10));
|
|
83
|
+
|
|
84
|
+
// Build contact propensity matrix
|
|
85
|
+
const contactMatrix = this.computeContactPropensity(proteinPrimes);
|
|
86
|
+
|
|
87
|
+
// Initialize phases randomly
|
|
88
|
+
const phases = new Array(n).fill(0).map(() => Math.random() * 2 * Math.PI);
|
|
89
|
+
|
|
90
|
+
// Evolution tracking
|
|
91
|
+
const history = {
|
|
92
|
+
orderParameter: [],
|
|
93
|
+
entropy: [],
|
|
94
|
+
energyLandscape: []
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Kuramoto evolution
|
|
98
|
+
let order = 0;
|
|
99
|
+
let step = 0;
|
|
100
|
+
|
|
101
|
+
for (step = 0; step < this.options.maxSteps; step++) {
|
|
102
|
+
// Compute phase updates (Kuramoto equation)
|
|
103
|
+
const dPhases = this.kuramotoStep(phases, frequencies, contactMatrix);
|
|
104
|
+
|
|
105
|
+
// Apply thermal noise
|
|
106
|
+
for (let i = 0; i < n; i++) {
|
|
107
|
+
phases[i] += dPhases[i] * this.options.dt;
|
|
108
|
+
phases[i] += this.thermalNoise() * Math.sqrt(this.options.dt);
|
|
109
|
+
// Keep phases in [0, 2π]
|
|
110
|
+
phases[i] = ((phases[i] % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Calculate order parameter
|
|
114
|
+
order = this.calculateOrderParameter(phases);
|
|
115
|
+
const entropy = this.calculatePhasesEntropy(phases);
|
|
116
|
+
const energy = this.calculateFoldingEnergy(phases, proteinPrimes, contactMatrix);
|
|
117
|
+
|
|
118
|
+
history.orderParameter.push(order);
|
|
119
|
+
history.entropy.push(entropy);
|
|
120
|
+
history.energyLandscape.push(energy);
|
|
121
|
+
|
|
122
|
+
// Check convergence
|
|
123
|
+
if (order > this.options.convergenceThreshold) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Extract structure from final phases
|
|
129
|
+
const structure = this.phasesToStructure(phases, proteinPrimes);
|
|
130
|
+
const contacts = this.extractContacts(phases, this.options.minSequenceSeparation);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
success: true,
|
|
134
|
+
phases: [...phases],
|
|
135
|
+
structure,
|
|
136
|
+
contacts,
|
|
137
|
+
orderParameter: order,
|
|
138
|
+
finalEntropy: history.entropy[history.entropy.length - 1],
|
|
139
|
+
foldingFreeEnergy: this.estimateFreeEnergy(order, proteinPrimes),
|
|
140
|
+
stepsToConverge: step,
|
|
141
|
+
history,
|
|
142
|
+
secondaryStructure: this.assignSecondaryStructure(structure),
|
|
143
|
+
compactness: this.calculateCompactness(contacts, n)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Compute Kuramoto phase derivatives
|
|
149
|
+
*/
|
|
150
|
+
kuramotoStep(phases, frequencies, coupling) {
|
|
151
|
+
const n = phases.length;
|
|
152
|
+
const dPhases = new Array(n).fill(0);
|
|
153
|
+
|
|
154
|
+
for (let i = 0; i < n; i++) {
|
|
155
|
+
// Natural frequency term
|
|
156
|
+
dPhases[i] = frequencies[i];
|
|
157
|
+
|
|
158
|
+
// Coupling term (sum over all connected oscillators)
|
|
159
|
+
for (let j = 0; j < n; j++) {
|
|
160
|
+
if (i !== j && coupling[i][j] > 0) {
|
|
161
|
+
dPhases[i] += coupling[i][j] * Math.sin(phases[j] - phases[i]);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return dPhases;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Compute contact propensity matrix from amino acid properties
|
|
171
|
+
*/
|
|
172
|
+
computeContactPropensity(proteinPrimes) {
|
|
173
|
+
const n = proteinPrimes.length;
|
|
174
|
+
const matrix = Array(n).fill(null).map(() => Array(n).fill(0));
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < n; i++) {
|
|
177
|
+
for (let j = i + this.options.minSequenceSeparation; j < n; j++) {
|
|
178
|
+
const pi = proteinPrimes[i];
|
|
179
|
+
const pj = proteinPrimes[j];
|
|
180
|
+
|
|
181
|
+
// Resonance component (golden ratio proximity)
|
|
182
|
+
const resonance = calculateResonance(pi, pj);
|
|
183
|
+
|
|
184
|
+
// Hydrophobic interaction (smaller primes are more hydrophobic)
|
|
185
|
+
const hi = getHydrophobicityFromPrime(pi);
|
|
186
|
+
const hj = getHydrophobicityFromPrime(pj);
|
|
187
|
+
const hydrophobic = this.hydrophobicPotential(hi, hj);
|
|
188
|
+
|
|
189
|
+
// Electrostatic interaction
|
|
190
|
+
const qi = getChargeFromPrime(pi);
|
|
191
|
+
const qj = getChargeFromPrime(pj);
|
|
192
|
+
const electrostatic = this.electrostaticPotential(qi, qj);
|
|
193
|
+
|
|
194
|
+
// Combined contact propensity
|
|
195
|
+
const propensity =
|
|
196
|
+
this.options.resonanceWeight * resonance +
|
|
197
|
+
this.options.hydrophobicWeight * hydrophobic +
|
|
198
|
+
this.options.electrostaticWeight * electrostatic;
|
|
199
|
+
|
|
200
|
+
matrix[i][j] = matrix[j][i] = Math.max(0, propensity) * this.options.coupling;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return matrix;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Hydrophobic potential: like attracts like
|
|
209
|
+
*/
|
|
210
|
+
hydrophobicPotential(h1, h2) {
|
|
211
|
+
// Both hydrophobic (positive h): attract
|
|
212
|
+
// Both hydrophilic (negative h): weak attract
|
|
213
|
+
// Mixed: repel
|
|
214
|
+
if (h1 > 0 && h2 > 0) {
|
|
215
|
+
return Math.sqrt(h1 * h2) / 4;
|
|
216
|
+
} else if (h1 < 0 && h2 < 0) {
|
|
217
|
+
return Math.sqrt(-h1 * -h2) / 8;
|
|
218
|
+
} else {
|
|
219
|
+
return -0.1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Electrostatic potential: opposites attract
|
|
225
|
+
*/
|
|
226
|
+
electrostaticPotential(q1, q2) {
|
|
227
|
+
if (q1 * q2 < 0) {
|
|
228
|
+
return 0.5; // Opposite charges attract
|
|
229
|
+
} else if (q1 * q2 > 0) {
|
|
230
|
+
return -0.3; // Same charges repel
|
|
231
|
+
}
|
|
232
|
+
return 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Generate thermal noise
|
|
237
|
+
*/
|
|
238
|
+
thermalNoise() {
|
|
239
|
+
// Box-Muller transform for Gaussian noise
|
|
240
|
+
const u1 = Math.random();
|
|
241
|
+
const u2 = Math.random();
|
|
242
|
+
const gaussian = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
243
|
+
return gaussian * Math.sqrt(this.options.temperature) * 0.1;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Calculate Kuramoto order parameter
|
|
248
|
+
*/
|
|
249
|
+
calculateOrderParameter(phases) {
|
|
250
|
+
const n = phases.length;
|
|
251
|
+
let sumCos = 0, sumSin = 0;
|
|
252
|
+
|
|
253
|
+
for (const phase of phases) {
|
|
254
|
+
sumCos += Math.cos(phase);
|
|
255
|
+
sumSin += Math.sin(phase);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return Math.sqrt(sumCos * sumCos + sumSin * sumSin) / n;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Calculate entropy of phase distribution
|
|
263
|
+
*/
|
|
264
|
+
calculatePhasesEntropy(phases) {
|
|
265
|
+
const bins = 36; // 10-degree bins
|
|
266
|
+
const histogram = Array(bins).fill(0);
|
|
267
|
+
|
|
268
|
+
for (const phase of phases) {
|
|
269
|
+
const bin = Math.floor(phase / (2 * Math.PI) * bins);
|
|
270
|
+
histogram[Math.min(bin, bins - 1)]++;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const probs = histogram.map(h => h / phases.length);
|
|
274
|
+
return shannonEntropy(probs);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Calculate folding energy from phases and contacts
|
|
279
|
+
*/
|
|
280
|
+
calculateFoldingEnergy(phases, primes, contactMatrix) {
|
|
281
|
+
let energy = 0;
|
|
282
|
+
const n = phases.length;
|
|
283
|
+
|
|
284
|
+
for (let i = 0; i < n; i++) {
|
|
285
|
+
for (let j = i + this.options.minSequenceSeparation; j < n; j++) {
|
|
286
|
+
const phaseDiff = Math.abs(phases[i] - phases[j]);
|
|
287
|
+
const inContact = phaseDiff < 0.5 || phaseDiff > 2 * Math.PI - 0.5;
|
|
288
|
+
|
|
289
|
+
if (inContact && contactMatrix[i][j] > 0) {
|
|
290
|
+
// Favorable contact
|
|
291
|
+
energy -= contactMatrix[i][j];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return energy;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Convert phases to structural information
|
|
301
|
+
*/
|
|
302
|
+
phasesToStructure(phases, primes) {
|
|
303
|
+
const structure = [];
|
|
304
|
+
|
|
305
|
+
for (let i = 0; i < phases.length; i++) {
|
|
306
|
+
// Phase determines backbone angles (phi, psi approximation)
|
|
307
|
+
const phi = (phases[i] - Math.PI) * 180 / Math.PI;
|
|
308
|
+
const nextPhase = phases[(i + 1) % phases.length];
|
|
309
|
+
const psi = (nextPhase - phases[i]) * 180 / Math.PI;
|
|
310
|
+
|
|
311
|
+
structure.push({
|
|
312
|
+
residue: i,
|
|
313
|
+
prime: primes[i],
|
|
314
|
+
phase: phases[i],
|
|
315
|
+
phi,
|
|
316
|
+
psi,
|
|
317
|
+
secondaryStructure: this.classifySecondaryStructure(phi, psi)
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return structure;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Classify secondary structure from backbone angles
|
|
326
|
+
*/
|
|
327
|
+
classifySecondaryStructure(phi, psi) {
|
|
328
|
+
// Alpha helix: phi ≈ -60, psi ≈ -45
|
|
329
|
+
if (phi > -100 && phi < -30 && psi > -70 && psi < 0) {
|
|
330
|
+
return 'H'; // Helix
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Beta sheet: phi ≈ -120, psi ≈ +120
|
|
334
|
+
if (phi > -180 && phi < -60 && (psi > 90 || psi < -90)) {
|
|
335
|
+
return 'E'; // Extended (sheet)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 3-10 helix: phi ≈ -49, psi ≈ -26
|
|
339
|
+
if (phi > -70 && phi < -30 && psi > -50 && psi < 0) {
|
|
340
|
+
return 'G'; // 3-10 helix
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return 'C'; // Coil
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Extract residue-residue contacts from phases
|
|
348
|
+
*/
|
|
349
|
+
extractContacts(phases, minSeparation = 4, threshold = 0.5) {
|
|
350
|
+
const contacts = [];
|
|
351
|
+
const n = phases.length;
|
|
352
|
+
|
|
353
|
+
for (let i = 0; i < n; i++) {
|
|
354
|
+
for (let j = i + minSeparation; j < n; j++) {
|
|
355
|
+
const phaseDiff = Math.abs(phases[i] - phases[j]);
|
|
356
|
+
const normalized = Math.min(phaseDiff, 2 * Math.PI - phaseDiff);
|
|
357
|
+
|
|
358
|
+
if (normalized < threshold) {
|
|
359
|
+
contacts.push({
|
|
360
|
+
i,
|
|
361
|
+
j,
|
|
362
|
+
distance: normalized,
|
|
363
|
+
strength: 1 - normalized / threshold
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return contacts;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Assign secondary structure elements
|
|
374
|
+
*/
|
|
375
|
+
assignSecondaryStructure(structure) {
|
|
376
|
+
const ssSequence = structure.map(s => s.secondaryStructure).join('');
|
|
377
|
+
|
|
378
|
+
// Find helices (runs of H)
|
|
379
|
+
const helices = [];
|
|
380
|
+
const helixPattern = /H{4,}/g;
|
|
381
|
+
let match;
|
|
382
|
+
while ((match = helixPattern.exec(ssSequence)) !== null) {
|
|
383
|
+
helices.push({ start: match.index, end: match.index + match[0].length - 1, type: 'helix' });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Find sheets (runs of E)
|
|
387
|
+
const sheets = [];
|
|
388
|
+
const sheetPattern = /E{3,}/g;
|
|
389
|
+
while ((match = sheetPattern.exec(ssSequence)) !== null) {
|
|
390
|
+
sheets.push({ start: match.index, end: match.index + match[0].length - 1, type: 'sheet' });
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
sequence: ssSequence,
|
|
395
|
+
helices,
|
|
396
|
+
sheets,
|
|
397
|
+
helixContent: (ssSequence.match(/H/g) || []).length / structure.length,
|
|
398
|
+
sheetContent: (ssSequence.match(/E/g) || []).length / structure.length
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Calculate compactness (contact order)
|
|
404
|
+
*/
|
|
405
|
+
calculateCompactness(contacts, length) {
|
|
406
|
+
if (contacts.length === 0) return 0;
|
|
407
|
+
|
|
408
|
+
const totalSeparation = contacts.reduce((sum, c) => sum + (c.j - c.i), 0);
|
|
409
|
+
return totalSeparation / (contacts.length * length);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Estimate folding free energy
|
|
414
|
+
*/
|
|
415
|
+
estimateFreeEnergy(orderParameter, primes) {
|
|
416
|
+
// ΔG ∝ -kT * ln(order parameter) - hydrophobic contribution
|
|
417
|
+
const kT = this.options.temperature;
|
|
418
|
+
const entropyTerm = -kT * Math.log(Math.max(orderParameter, 0.01));
|
|
419
|
+
|
|
420
|
+
// Hydrophobic burial contribution
|
|
421
|
+
const hydrophobicPrimes = primes.filter(p => p <= 43);
|
|
422
|
+
const hydrophobicFraction = hydrophobicPrimes.length / primes.length;
|
|
423
|
+
const hydrophobicTerm = -hydrophobicFraction * 2.0; // Favorable
|
|
424
|
+
|
|
425
|
+
return entropyTerm + hydrophobicTerm;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Simulated annealing for global minimum
|
|
430
|
+
*/
|
|
431
|
+
anneal(proteinPrimes, options = {}) {
|
|
432
|
+
const startTemp = options.startTemp || 10.0;
|
|
433
|
+
const endTemp = options.endTemp || 0.1;
|
|
434
|
+
const coolingRate = options.coolingRate || 0.99;
|
|
435
|
+
|
|
436
|
+
let temp = startTemp;
|
|
437
|
+
let bestResult = null;
|
|
438
|
+
|
|
439
|
+
while (temp > endTemp) {
|
|
440
|
+
this.options.temperature = temp;
|
|
441
|
+
const result = this.fold(proteinPrimes);
|
|
442
|
+
|
|
443
|
+
if (!bestResult || result.foldingFreeEnergy < bestResult.foldingFreeEnergy) {
|
|
444
|
+
bestResult = result;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
temp *= coolingRate;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return bestResult;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
module.exports = { FoldingTransform };
|