@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
package/core/compound.js
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compound Builder
|
|
3
|
+
*
|
|
4
|
+
* Creates and manages multi-symbol concepts through prime multiplication.
|
|
5
|
+
* Enables cultural modulation - the same base concept with different cultural overlays.
|
|
6
|
+
*
|
|
7
|
+
* Example:
|
|
8
|
+
* greek_warrior = warrior × temple × eagle
|
|
9
|
+
* viking_warrior = warrior × ocean × shield
|
|
10
|
+
* samurai_warrior = warrior × garden × monk
|
|
11
|
+
*
|
|
12
|
+
* All share the warrior archetype but with cultural differentiation.
|
|
13
|
+
*
|
|
14
|
+
* Ported from symprime's CompoundBuilder system.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const { symbolDatabase, SymbolCategory } = require('./symbols');
|
|
18
|
+
const { ResonanceCalculator } = require('./resonance');
|
|
19
|
+
|
|
20
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
21
|
+
// Compound Symbol Type
|
|
22
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A compound symbol is a combination of multiple base symbols
|
|
26
|
+
* with a unified meaning and cultural context.
|
|
27
|
+
*/
|
|
28
|
+
class CompoundSymbol {
|
|
29
|
+
constructor(id, components, meaning, culturalTags = []) {
|
|
30
|
+
this.id = id;
|
|
31
|
+
this.components = components; // Array of base symbols
|
|
32
|
+
this.meaning = meaning;
|
|
33
|
+
this.culturalTags = culturalTags;
|
|
34
|
+
|
|
35
|
+
// Calculate combined prime signature (product of primes)
|
|
36
|
+
this.prime = this.calculatePrime();
|
|
37
|
+
|
|
38
|
+
// Combined unicode representation
|
|
39
|
+
this.unicode = components.map(c => c.unicode).join('');
|
|
40
|
+
|
|
41
|
+
// Metadata
|
|
42
|
+
this.metadata = {
|
|
43
|
+
componentCount: components.length,
|
|
44
|
+
componentIds: components.map(c => c.id),
|
|
45
|
+
createdAt: Date.now()
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
calculatePrime() {
|
|
50
|
+
let product = 1n;
|
|
51
|
+
for (const component of this.components) {
|
|
52
|
+
product *= BigInt(component.prime);
|
|
53
|
+
}
|
|
54
|
+
return product;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
toJSON() {
|
|
58
|
+
return {
|
|
59
|
+
id: this.id,
|
|
60
|
+
unicode: this.unicode,
|
|
61
|
+
prime: this.prime.toString(),
|
|
62
|
+
meaning: this.meaning,
|
|
63
|
+
culturalTags: this.culturalTags,
|
|
64
|
+
components: this.components.map(c => c.id),
|
|
65
|
+
metadata: this.metadata
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
toString() {
|
|
70
|
+
return `${this.unicode} (${this.id}): ${this.meaning}`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
75
|
+
// Symbol Sequence Type
|
|
76
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A symbol sequence represents a narrative or temporal ordering
|
|
80
|
+
* of symbols (e.g., hero's journey stages).
|
|
81
|
+
*/
|
|
82
|
+
class SymbolSequence {
|
|
83
|
+
constructor(id, symbols, type, description = '') {
|
|
84
|
+
this.id = id;
|
|
85
|
+
this.symbols = symbols; // Ordered array of symbols
|
|
86
|
+
this.type = type; // 'narrative', 'journey', 'transformation', etc.
|
|
87
|
+
this.description = description;
|
|
88
|
+
|
|
89
|
+
// Combined signature (product of primes)
|
|
90
|
+
this.signature = this.calculateSignature();
|
|
91
|
+
|
|
92
|
+
// Temporal metadata
|
|
93
|
+
this.temporal = {
|
|
94
|
+
order: symbols.map((_, i) => i),
|
|
95
|
+
duration: null,
|
|
96
|
+
startTime: null
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
calculateSignature() {
|
|
101
|
+
let product = 1n;
|
|
102
|
+
for (const symbol of this.symbols) {
|
|
103
|
+
product *= BigInt(symbol.prime);
|
|
104
|
+
}
|
|
105
|
+
return product;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
toJSON() {
|
|
109
|
+
return {
|
|
110
|
+
id: this.id,
|
|
111
|
+
type: this.type,
|
|
112
|
+
description: this.description,
|
|
113
|
+
symbols: this.symbols.map(s => s.id),
|
|
114
|
+
signature: this.signature.toString(),
|
|
115
|
+
temporal: this.temporal
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
121
|
+
// Compound Builder
|
|
122
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
123
|
+
|
|
124
|
+
class CompoundBuilder {
|
|
125
|
+
constructor(database = symbolDatabase) {
|
|
126
|
+
this.database = database;
|
|
127
|
+
this.compounds = new Map(); // id → CompoundSymbol
|
|
128
|
+
this.sequences = new Map(); // id → SymbolSequence
|
|
129
|
+
this.resonanceCalc = new ResonanceCalculator();
|
|
130
|
+
|
|
131
|
+
this.initializeCommonCompounds();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Create a compound symbol from multiple base symbols
|
|
136
|
+
*/
|
|
137
|
+
createCompound(id, componentIds, meaning, culturalTags = []) {
|
|
138
|
+
const components = [];
|
|
139
|
+
for (const cid of componentIds) {
|
|
140
|
+
const symbol = this.database.getSymbol(cid);
|
|
141
|
+
if (!symbol) {
|
|
142
|
+
throw new Error(`Unknown symbol: ${cid}`);
|
|
143
|
+
}
|
|
144
|
+
components.push(symbol);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (components.length === 0) {
|
|
148
|
+
throw new Error('Compound must have at least one component');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const compound = new CompoundSymbol(id, components, meaning, culturalTags);
|
|
152
|
+
this.compounds.set(id, compound);
|
|
153
|
+
return compound;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Create compound from symbol objects directly
|
|
158
|
+
*/
|
|
159
|
+
createCompoundFromSymbols(id, components, meaning, culturalTags = []) {
|
|
160
|
+
if (!components || components.length === 0) {
|
|
161
|
+
throw new Error('Compound must have at least one component');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const compound = new CompoundSymbol(id, components, meaning, culturalTags);
|
|
165
|
+
this.compounds.set(id, compound);
|
|
166
|
+
return compound;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get a compound by ID
|
|
171
|
+
*/
|
|
172
|
+
getCompound(id) {
|
|
173
|
+
return this.compounds.get(id);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check if compound exists
|
|
178
|
+
*/
|
|
179
|
+
hasCompound(id) {
|
|
180
|
+
return this.compounds.has(id);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Decompose a compound into its components
|
|
185
|
+
*/
|
|
186
|
+
decompose(compound) {
|
|
187
|
+
return [...compound.components];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Create a symbol sequence (narrative)
|
|
192
|
+
*/
|
|
193
|
+
createSequence(id, symbolIds, type, description = '') {
|
|
194
|
+
const symbols = [];
|
|
195
|
+
for (const sid of symbolIds) {
|
|
196
|
+
const symbol = this.database.getSymbol(sid);
|
|
197
|
+
if (!symbol) {
|
|
198
|
+
throw new Error(`Unknown symbol: ${sid}`);
|
|
199
|
+
}
|
|
200
|
+
symbols.push(symbol);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (symbols.length === 0) {
|
|
204
|
+
throw new Error('Sequence must have at least one symbol');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const sequence = new SymbolSequence(id, symbols, type, description);
|
|
208
|
+
this.sequences.set(id, sequence);
|
|
209
|
+
return sequence;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get a sequence by ID
|
|
214
|
+
*/
|
|
215
|
+
getSequence(id) {
|
|
216
|
+
return this.sequences.get(id);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Merge two compounds into a larger compound
|
|
221
|
+
*/
|
|
222
|
+
mergeCompounds(id, compound1, compound2, meaning) {
|
|
223
|
+
const allComponents = [...compound1.components, ...compound2.components];
|
|
224
|
+
const allTags = [...new Set([...compound1.culturalTags, ...compound2.culturalTags])];
|
|
225
|
+
|
|
226
|
+
return this.createCompoundFromSymbols(
|
|
227
|
+
id,
|
|
228
|
+
allComponents,
|
|
229
|
+
meaning || `${compound1.meaning} + ${compound2.meaning}`,
|
|
230
|
+
allTags
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Create cultural variant of a base compound
|
|
236
|
+
*/
|
|
237
|
+
createCulturalVariant(baseId, culture, additionalSymbolIds, meaning) {
|
|
238
|
+
const base = this.getCompound(baseId);
|
|
239
|
+
if (!base) {
|
|
240
|
+
throw new Error(`Unknown base compound: ${baseId}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const additionalSymbols = additionalSymbolIds.map(id => {
|
|
244
|
+
const s = this.database.getSymbol(id);
|
|
245
|
+
if (!s) throw new Error(`Unknown symbol: ${id}`);
|
|
246
|
+
return s;
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const allComponents = [...base.components, ...additionalSymbols];
|
|
250
|
+
const variantId = `${baseId}_${culture}`;
|
|
251
|
+
|
|
252
|
+
return this.createCompoundFromSymbols(
|
|
253
|
+
variantId,
|
|
254
|
+
allComponents,
|
|
255
|
+
meaning,
|
|
256
|
+
[culture, ...base.culturalTags]
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Initialize common compound archetypes
|
|
262
|
+
*/
|
|
263
|
+
initializeCommonCompounds() {
|
|
264
|
+
try {
|
|
265
|
+
// Greek Warrior
|
|
266
|
+
const warrior = this.database.getSymbol('warrior');
|
|
267
|
+
const temple = this.database.getSymbol('temple');
|
|
268
|
+
const athena = this.database.getSymbol('athena');
|
|
269
|
+
|
|
270
|
+
if (warrior && temple && athena) {
|
|
271
|
+
this.createCompoundFromSymbols(
|
|
272
|
+
'greek_warrior',
|
|
273
|
+
[warrior, temple, athena],
|
|
274
|
+
'Greek Warrior - Temple guardian blessed by Athena',
|
|
275
|
+
['greek', 'warrior', 'mythology']
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Viking Warrior
|
|
280
|
+
const ocean = this.database.getSymbol('ocean');
|
|
281
|
+
const shield = this.database.getSymbol('shield');
|
|
282
|
+
|
|
283
|
+
if (warrior && ocean && shield) {
|
|
284
|
+
this.createCompoundFromSymbols(
|
|
285
|
+
'viking_warrior',
|
|
286
|
+
[warrior, ocean, shield],
|
|
287
|
+
'Viking Warrior - Sea raider with shield',
|
|
288
|
+
['norse', 'warrior', 'mythology']
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Samurai Warrior
|
|
293
|
+
const samurai = this.database.getSymbol('samurai');
|
|
294
|
+
const garden = this.database.getSymbol('garden');
|
|
295
|
+
const monk = this.database.getSymbol('monk');
|
|
296
|
+
|
|
297
|
+
if (samurai && garden && monk) {
|
|
298
|
+
this.createCompoundFromSymbols(
|
|
299
|
+
'samurai_warrior',
|
|
300
|
+
[samurai, garden, monk],
|
|
301
|
+
'Samurai Warrior - Disciplined warrior of the cherry blossoms',
|
|
302
|
+
['japanese', 'warrior', 'honor']
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Philosopher King
|
|
307
|
+
const king = this.database.getSymbol('king');
|
|
308
|
+
const sage = this.database.getSymbol('sage');
|
|
309
|
+
const book = this.database.getSymbol('book');
|
|
310
|
+
|
|
311
|
+
if (king && sage && book) {
|
|
312
|
+
this.createCompoundFromSymbols(
|
|
313
|
+
'philosopher_king',
|
|
314
|
+
[king, sage, book],
|
|
315
|
+
'Philosopher King - Wise ruler guided by knowledge',
|
|
316
|
+
['greek', 'leadership', 'wisdom']
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Shadow Self
|
|
321
|
+
const shadow = this.database.getSymbol('shadow');
|
|
322
|
+
const mirror = this.database.getSymbol('mirror');
|
|
323
|
+
|
|
324
|
+
if (shadow && mirror) {
|
|
325
|
+
this.createCompoundFromSymbols(
|
|
326
|
+
'shadow_self',
|
|
327
|
+
[shadow, mirror],
|
|
328
|
+
'Shadow Self - The dark reflection within',
|
|
329
|
+
['universal', 'psychology', 'jungian']
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Hero's Journey (sequence)
|
|
334
|
+
const hero = this.database.getSymbol('hero');
|
|
335
|
+
const path = this.database.getSymbol('path');
|
|
336
|
+
const labyrinth = this.database.getSymbol('labyrinth');
|
|
337
|
+
const destroyer = this.database.getSymbol('destroyer');
|
|
338
|
+
const transformation = this.database.getSymbol('transformation');
|
|
339
|
+
|
|
340
|
+
if (hero && path && labyrinth && destroyer && transformation) {
|
|
341
|
+
this.createSequence(
|
|
342
|
+
'heros_journey',
|
|
343
|
+
['hero', 'path', 'labyrinth', 'destroyer', 'transformation'],
|
|
344
|
+
'journey',
|
|
345
|
+
'The monomyth pattern of departure, trials, and return'
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Alchemy Transformation (sequence)
|
|
350
|
+
const fire = this.database.getSymbol('fire');
|
|
351
|
+
const water = this.database.getSymbol('water');
|
|
352
|
+
const gold = this.database.getSymbol('gold');
|
|
353
|
+
|
|
354
|
+
if (fire && water && gold) {
|
|
355
|
+
this.createSequence(
|
|
356
|
+
'alchemical_transformation',
|
|
357
|
+
['fire', 'water', 'gold'],
|
|
358
|
+
'transformation',
|
|
359
|
+
'Base elements transmuted to gold through fire and water'
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
} catch (error) {
|
|
364
|
+
console.debug('Some compound symbols could not be initialized:', error.message);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Find compounds containing a specific symbol
|
|
370
|
+
*/
|
|
371
|
+
findCompoundsContaining(symbolId) {
|
|
372
|
+
const results = [];
|
|
373
|
+
for (const compound of this.compounds.values()) {
|
|
374
|
+
if (compound.components.some(c => c.id === symbolId)) {
|
|
375
|
+
results.push(compound);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Find sequences containing a specific symbol
|
|
383
|
+
*/
|
|
384
|
+
findSequencesContaining(symbolId) {
|
|
385
|
+
const results = [];
|
|
386
|
+
for (const sequence of this.sequences.values()) {
|
|
387
|
+
if (sequence.symbols.some(s => s.id === symbolId)) {
|
|
388
|
+
results.push(sequence);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return results;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Calculate internal resonance of a compound
|
|
396
|
+
* (How harmoniously do its components relate?)
|
|
397
|
+
*/
|
|
398
|
+
calculateCompoundResonance(compound) {
|
|
399
|
+
if (compound.components.length < 2) {
|
|
400
|
+
return 1.0; // Single component has perfect self-resonance
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
let totalResonance = 0;
|
|
404
|
+
let pairCount = 0;
|
|
405
|
+
|
|
406
|
+
for (let i = 0; i < compound.components.length; i++) {
|
|
407
|
+
for (let j = i + 1; j < compound.components.length; j++) {
|
|
408
|
+
const r = this.resonanceCalc.calculateResonance(
|
|
409
|
+
compound.components[i].prime,
|
|
410
|
+
compound.components[j].prime
|
|
411
|
+
);
|
|
412
|
+
totalResonance += r;
|
|
413
|
+
pairCount++;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return pairCount > 0 ? totalResonance / pairCount : 0;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Find the most resonant addition to a compound
|
|
422
|
+
*/
|
|
423
|
+
findResonantAddition(compound, candidateIds) {
|
|
424
|
+
let bestSymbol = null;
|
|
425
|
+
let bestResonance = -1;
|
|
426
|
+
|
|
427
|
+
for (const cid of candidateIds) {
|
|
428
|
+
const symbol = this.database.getSymbol(cid);
|
|
429
|
+
if (!symbol) continue;
|
|
430
|
+
|
|
431
|
+
// Calculate average resonance with existing components
|
|
432
|
+
let totalR = 0;
|
|
433
|
+
for (const comp of compound.components) {
|
|
434
|
+
totalR += this.resonanceCalc.calculateResonance(symbol.prime, comp.prime);
|
|
435
|
+
}
|
|
436
|
+
const avgR = totalR / compound.components.length;
|
|
437
|
+
|
|
438
|
+
if (avgR > bestResonance) {
|
|
439
|
+
bestResonance = avgR;
|
|
440
|
+
bestSymbol = symbol;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return bestSymbol ? { symbol: bestSymbol, resonance: bestResonance } : null;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Get all registered compounds
|
|
449
|
+
*/
|
|
450
|
+
getAllCompounds() {
|
|
451
|
+
return Array.from(this.compounds.values());
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Get all registered sequences
|
|
456
|
+
*/
|
|
457
|
+
getAllSequences() {
|
|
458
|
+
return Array.from(this.sequences.values());
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Clear all compounds (keeps defaults on next init)
|
|
463
|
+
*/
|
|
464
|
+
clearCompounds() {
|
|
465
|
+
this.compounds.clear();
|
|
466
|
+
this.initializeCommonCompounds();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Clear all sequences
|
|
471
|
+
*/
|
|
472
|
+
clearSequences() {
|
|
473
|
+
this.sequences.clear();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Get statistics
|
|
478
|
+
*/
|
|
479
|
+
getStats() {
|
|
480
|
+
const compounds = this.getAllCompounds();
|
|
481
|
+
const sequences = this.getAllSequences();
|
|
482
|
+
|
|
483
|
+
const avgComponents = compounds.length > 0
|
|
484
|
+
? compounds.reduce((sum, c) => sum + c.components.length, 0) / compounds.length
|
|
485
|
+
: 0;
|
|
486
|
+
|
|
487
|
+
const avgSymbols = sequences.length > 0
|
|
488
|
+
? sequences.reduce((sum, s) => sum + s.symbols.length, 0) / sequences.length
|
|
489
|
+
: 0;
|
|
490
|
+
|
|
491
|
+
return {
|
|
492
|
+
totalCompounds: compounds.length,
|
|
493
|
+
totalSequences: sequences.length,
|
|
494
|
+
avgComponentsPerCompound: avgComponents,
|
|
495
|
+
avgSymbolsPerSequence: avgSymbols,
|
|
496
|
+
cultureTags: this.getCultureDistribution()
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Get distribution of cultural tags across compounds
|
|
502
|
+
*/
|
|
503
|
+
getCultureDistribution() {
|
|
504
|
+
const dist = {};
|
|
505
|
+
for (const compound of this.compounds.values()) {
|
|
506
|
+
for (const tag of compound.culturalTags) {
|
|
507
|
+
dist[tag] = (dist[tag] || 0) + 1;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return dist;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Singleton instance
|
|
515
|
+
const defaultBuilder = new CompoundBuilder();
|
|
516
|
+
|
|
517
|
+
module.exports = {
|
|
518
|
+
CompoundBuilder,
|
|
519
|
+
CompoundSymbol,
|
|
520
|
+
SymbolSequence,
|
|
521
|
+
compoundBuilder: defaultBuilder,
|
|
522
|
+
|
|
523
|
+
// Convenience functions
|
|
524
|
+
createCompound: (id, components, meaning, tags) =>
|
|
525
|
+
defaultBuilder.createCompound(id, components, meaning, tags),
|
|
526
|
+
getCompound: (id) => defaultBuilder.getCompound(id),
|
|
527
|
+
createSequence: (id, symbols, type, desc) =>
|
|
528
|
+
defaultBuilder.createSequence(id, symbols, type, desc),
|
|
529
|
+
getSequence: (id) => defaultBuilder.getSequence(id),
|
|
530
|
+
findCompoundsContaining: (symbolId) =>
|
|
531
|
+
defaultBuilder.findCompoundsContaining(symbolId)
|
|
532
|
+
};
|