mycai 1.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/LICENSE +23 -0
- package/README.md +119 -0
- package/bin/mycai.js +156 -0
- package/core/ghr_lattice_memory.js +401 -0
- package/core/hdc.js +312 -0
- package/core/memory.js +60 -0
- package/core/memory_engine.js +444 -0
- package/core/morphology.js +357 -0
- package/core/phase.js +87 -0
- package/core/reasoning_router.js +628 -0
- package/core/resonance.js +118 -0
- package/core/safetensors.js +62 -0
- package/core/sovereign_agent.js +164 -0
- package/core/spectral.js +259 -0
- package/core/spectral_decoder.js +868 -0
- package/dist/resonance_sdk.js +2703 -0
- package/dist/resonance_sdk.min.js +14 -0
- package/dist/spectral_core.wasm +0 -0
- package/index.d.ts +45 -0
- package/index.js +26 -0
- package/package.json +52 -0
- package/sdk/README.md +154 -0
- package/sdk/examples/banking_assistant.js +61 -0
- package/sdk/examples/browser_example.html +104 -0
- package/sdk/examples/express_integration.js +61 -0
- package/sdk/examples/node_example.js +33 -0
- package/sdk/examples/quickstart.js +55 -0
- package/sdk/index.js +22 -0
- package/sdk/ingestion.js +237 -0
- package/sdk/licensing.js +168 -0
- package/sdk/resonance_engine.js +594 -0
- package/sdk/resonance_sdk.js +792 -0
- package/sdk/storage.js +185 -0
- package/sdk/tests/sdk_verification.js +98 -0
- package/sdk/tests/test_commercial_sdk.js +136 -0
- package/sdk/types.d.ts +152 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resonance Engine Module
|
|
3
|
+
* Computes multi-dimensional resonance metrics combining Semantic, Morphology, Spectral, Phase, and Context.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { TurkishMorphology } from './morphology.js';
|
|
7
|
+
import { HDCEngine } from './hdc.js';
|
|
8
|
+
import { SpectralEngine } from './spectral.js';
|
|
9
|
+
import { PhaseEngine } from './phase.js';
|
|
10
|
+
|
|
11
|
+
export class ResonanceEngine {
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
// Configurable weights (default values as requested)
|
|
14
|
+
this.alpha = config.alpha !== undefined ? config.alpha : 0.30; // Semantic
|
|
15
|
+
this.beta = config.beta !== undefined ? config.beta : 0.20; // Morphology
|
|
16
|
+
this.gamma = config.gamma !== undefined ? config.gamma : 0.20; // Spectral
|
|
17
|
+
this.delta = config.delta !== undefined ? config.delta : 0.20; // Phase
|
|
18
|
+
this.epsilon = config.epsilon !== undefined ? config.epsilon : 0.10; // Context
|
|
19
|
+
|
|
20
|
+
this.morphology = new TurkishMorphology();
|
|
21
|
+
this.hdc = new HDCEngine();
|
|
22
|
+
this.spectral = new SpectralEngine();
|
|
23
|
+
this.phase = new PhaseEngine();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
updateWeights(config) {
|
|
27
|
+
if (config.alpha !== undefined) this.alpha = config.alpha;
|
|
28
|
+
if (config.beta !== undefined) this.beta = config.beta;
|
|
29
|
+
if (config.gamma !== undefined) this.gamma = config.gamma;
|
|
30
|
+
if (config.delta !== undefined) this.delta = config.delta;
|
|
31
|
+
if (config.epsilon !== undefined) this.epsilon = config.epsilon;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
computeResonance(query, candidate, queryRepr, candidateRepr) {
|
|
35
|
+
// 1. Semantic Score (cosine similarity in HDC domain)
|
|
36
|
+
const semanticScore = this.hdc.similarity(queryRepr, candidateRepr);
|
|
37
|
+
|
|
38
|
+
// 2. Morphology Score (root matches + suffix overlaps)
|
|
39
|
+
const morphQ = this.morphology.analyze(query);
|
|
40
|
+
const morphC = this.morphology.analyze(candidate);
|
|
41
|
+
let morphologyScore = 0.0;
|
|
42
|
+
|
|
43
|
+
if (morphQ.root === morphC.root) {
|
|
44
|
+
morphologyScore += 0.6; // heavy weighting for sharing the same root
|
|
45
|
+
}
|
|
46
|
+
// Calculate overlap of suffixes
|
|
47
|
+
const setQ = new Set(morphQ.suffixes);
|
|
48
|
+
const setC = new Set(morphC.suffixes);
|
|
49
|
+
let intersections = 0;
|
|
50
|
+
setQ.forEach(s => {
|
|
51
|
+
if (setC.has(s)) intersections++;
|
|
52
|
+
});
|
|
53
|
+
const unionSize = new Set([...setQ, ...setC]).size;
|
|
54
|
+
if (unionSize > 0) {
|
|
55
|
+
morphologyScore += 0.4 * (intersections / unionSize);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 3. Spectral Score (similarity of FFT magnitude spectra)
|
|
59
|
+
const specQ = this.spectral.spectralTransform(queryRepr);
|
|
60
|
+
const specC = this.spectral.spectralTransform(candidateRepr);
|
|
61
|
+
const spectralScore = this.spectral.spectralSimilarity(specQ, specC);
|
|
62
|
+
|
|
63
|
+
// 4. Phase Score (phase coherence between vectors)
|
|
64
|
+
const phaseScore = this.phase.phaseCoherence(queryRepr, candidateRepr);
|
|
65
|
+
|
|
66
|
+
// 5. Context Score (simple character-length ratio/exact overlaps)
|
|
67
|
+
let contextScore = 0.0;
|
|
68
|
+
if (morphQ.harmony === morphC.harmony) {
|
|
69
|
+
contextScore += 0.5; // same vowel harmony category
|
|
70
|
+
}
|
|
71
|
+
const lenDiff = Math.abs(query.length - candidate.length);
|
|
72
|
+
contextScore += 0.5 * Math.max(0, 1 - lenDiff / Math.max(query.length, candidate.length, 1));
|
|
73
|
+
|
|
74
|
+
// Calculate final weighted compound resonance
|
|
75
|
+
const finalScore =
|
|
76
|
+
this.alpha * semanticScore +
|
|
77
|
+
this.beta * morphologyScore +
|
|
78
|
+
this.gamma * spectralScore +
|
|
79
|
+
this.delta * phaseScore +
|
|
80
|
+
this.epsilon * contextScore;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
finalScore,
|
|
84
|
+
breakdown: {
|
|
85
|
+
semantic: semanticScore,
|
|
86
|
+
morphology: morphologyScore,
|
|
87
|
+
spectral: spectralScore,
|
|
88
|
+
phase: phaseScore,
|
|
89
|
+
context: contextScore
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
generateSignature(id, representation, text = '') {
|
|
95
|
+
const spec = this.spectral.spectralTransform(representation);
|
|
96
|
+
const energy = this.spectral.spectralEnergy(spec);
|
|
97
|
+
const entropy = this.spectral.spectralEntropy(spec);
|
|
98
|
+
const dominantFreq = this.spectral.extractDominantFrequencies(spec, 5);
|
|
99
|
+
|
|
100
|
+
const morph = this.morphology.analyze(text || id);
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
id,
|
|
104
|
+
dimension: representation.D,
|
|
105
|
+
amplitude: spec.magnitude,
|
|
106
|
+
phase: spec.phase,
|
|
107
|
+
dominant_frequencies: dominantFreq,
|
|
108
|
+
spectral_energy: energy,
|
|
109
|
+
spectral_entropy: entropy,
|
|
110
|
+
morphology_signature: {
|
|
111
|
+
root: morph.root,
|
|
112
|
+
morphemes: morph.morphemes,
|
|
113
|
+
features: morph.features
|
|
114
|
+
},
|
|
115
|
+
timestamp: Date.now()
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safetensors Parser Module
|
|
3
|
+
* Parsers HuggingFace .safetensors files natively in JavaScript.
|
|
4
|
+
* Read more: https://github.com/huggingface/safetensors
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
|
|
9
|
+
export class SafetensorsParser {
|
|
10
|
+
constructor(filePath) {
|
|
11
|
+
this.filePath = filePath;
|
|
12
|
+
this.fd = null;
|
|
13
|
+
this.header = {};
|
|
14
|
+
this.headerLength = 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
open() {
|
|
18
|
+
this.fd = fs.openSync(this.filePath, 'r');
|
|
19
|
+
|
|
20
|
+
// 1. Read first 8 bytes for header length (uint64 little endian)
|
|
21
|
+
const lenBuf = Buffer.alloc(8);
|
|
22
|
+
fs.readSync(this.fd, lenBuf, 0, 8, 0);
|
|
23
|
+
|
|
24
|
+
// Convert to number (safe for safe-tensors headers < 9PB)
|
|
25
|
+
this.headerLength = Number(lenBuf.readBigUInt64LE(0));
|
|
26
|
+
|
|
27
|
+
// 2. Read JSON header
|
|
28
|
+
const headerBuf = Buffer.alloc(this.headerLength);
|
|
29
|
+
fs.readSync(this.fd, headerBuf, 0, this.headerLength, 8);
|
|
30
|
+
this.header = JSON.parse(headerBuf.toString('utf8'));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getTensorNames() {
|
|
34
|
+
return Object.keys(this.header).filter(key => key !== '__metadata__');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
readTensor(tensorName) {
|
|
38
|
+
const meta = this.header[tensorName];
|
|
39
|
+
if (!meta) return null;
|
|
40
|
+
|
|
41
|
+
const [start, end] = meta.data_offsets;
|
|
42
|
+
const byteLength = end - start;
|
|
43
|
+
|
|
44
|
+
const dataBuf = Buffer.alloc(byteLength);
|
|
45
|
+
// Offset in file is: 8 (length field) + headerLength + start_offset
|
|
46
|
+
fs.readSync(this.fd, dataBuf, 0, byteLength, 8 + this.headerLength + start);
|
|
47
|
+
|
|
48
|
+
if (meta.dtype === 'F32') {
|
|
49
|
+
// Float32 arrays are 4 bytes per element
|
|
50
|
+
return new Float32Array(dataBuf.buffer, dataBuf.byteOffset, byteLength / 4);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return dataBuf;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
close() {
|
|
57
|
+
if (this.fd) {
|
|
58
|
+
fs.closeSync(this.fd);
|
|
59
|
+
this.fd = null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sovereign Autonomous Agent Module
|
|
3
|
+
* Implements a fully local ReAct (Reasoning + Acting) otonom loop
|
|
4
|
+
* powered by local tools and deterministic task planning.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { evaluateMath } from './reasoning_router.js';
|
|
8
|
+
|
|
9
|
+
export class SovereignAgent {
|
|
10
|
+
/**
|
|
11
|
+
* @param {Object} sdkInstance - The ResonanceSDK instance containing hdc, memory, router, morphology
|
|
12
|
+
*/
|
|
13
|
+
constructor(sdkInstance) {
|
|
14
|
+
this.sdk = sdkInstance;
|
|
15
|
+
this.memory = sdkInstance ? sdkInstance.memory : null;
|
|
16
|
+
this.morphology = sdkInstance ? sdkInstance.morphology : null;
|
|
17
|
+
this.router = sdkInstance ? sdkInstance.router : null;
|
|
18
|
+
|
|
19
|
+
// Wire up available otonom tools
|
|
20
|
+
this.tools = {
|
|
21
|
+
memory_lookup: (query) => {
|
|
22
|
+
if (!this.memory) return "Hata: Bellek motoru yüklü değil.";
|
|
23
|
+
const vec = this.router ? this.router.vectorize(query) : new Float32Array(1024);
|
|
24
|
+
const results = this.memory.retrieve(vec, 1);
|
|
25
|
+
if (results.length === 0) return "Bulunamadı.";
|
|
26
|
+
return results[0].record.content;
|
|
27
|
+
},
|
|
28
|
+
math_eval: (expr) => {
|
|
29
|
+
try {
|
|
30
|
+
return evaluateMath(expr).toString();
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return `Hata: Matematiksel hesaplama başarısız. ${e.message}`;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
morphology_analyze: (word) => {
|
|
36
|
+
if (!this.morphology) return "Hata: Morfoloji motoru yüklü değil.";
|
|
37
|
+
const analysis = this.morphology.analyze(word);
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
root: analysis.root,
|
|
40
|
+
suffixes: analysis.suffixes,
|
|
41
|
+
harmony: analysis.harmony,
|
|
42
|
+
syllables: analysis.syllables
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
spectral_transform: (text) => {
|
|
46
|
+
if (!this.sdk || !this.sdk.spectral) return "Hata: Spektral motor yüklü değil.";
|
|
47
|
+
const vec = this.router ? this.router.vectorize(text) : new Float32Array(1024);
|
|
48
|
+
const spec = this.sdk.spectral.spectralTransform({ type: 'real', values: vec, D: vec.length });
|
|
49
|
+
return `Energy: ${spec.magnitude.reduce((a, b) => a + b, 0).toFixed(2)}`;
|
|
50
|
+
},
|
|
51
|
+
system_time: () => {
|
|
52
|
+
return new Date().toISOString();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Run the ReAct autonomous execution loop
|
|
59
|
+
* @param {string} goal - The user prompt/objective
|
|
60
|
+
* @param {number} [maxSteps=5] - Maximum execution steps
|
|
61
|
+
* @returns {Promise<Object>} Execution log and final answer
|
|
62
|
+
*/
|
|
63
|
+
async execute(goal, maxSteps = 5) {
|
|
64
|
+
const logs = [];
|
|
65
|
+
let step = 1;
|
|
66
|
+
let finished = false;
|
|
67
|
+
let finalAnswer = "";
|
|
68
|
+
|
|
69
|
+
// Local planner parsing key intents from query
|
|
70
|
+
const lowerGoal = goal.toLowerCase();
|
|
71
|
+
|
|
72
|
+
while (step <= maxSteps && !finished) {
|
|
73
|
+
let thought = "";
|
|
74
|
+
let action = "";
|
|
75
|
+
let actionArg = "";
|
|
76
|
+
|
|
77
|
+
// Step-by-step reasoning path determined based on goal context
|
|
78
|
+
if (lowerGoal.includes("hatırla") || lowerGoal.includes("hafıza") || lowerGoal.includes("favori")) {
|
|
79
|
+
if (step === 1) {
|
|
80
|
+
thought = "Kullanıcının sorduğu favori veya bellek kaydını bulmak için hafızada arama yapmalıyım.";
|
|
81
|
+
action = "memory_lookup";
|
|
82
|
+
// extract likely key query
|
|
83
|
+
actionArg = lowerGoal.includes("renk") ? "favori renk" : "hafıza sorgusu";
|
|
84
|
+
} else if (step === 2) {
|
|
85
|
+
const prevObs = logs[0].observation;
|
|
86
|
+
thought = `Hafızadan '${prevObs}' bilgisini aldım. Bu kelimeyi morfolojik olarak analiz etmeliyim.`;
|
|
87
|
+
action = "morphology_analyze";
|
|
88
|
+
actionArg = prevObs.split(/\s+/).pop().replace(/[^a-zçgğıoöşuüâîû]/g, '');
|
|
89
|
+
} else {
|
|
90
|
+
const prevObs = logs[1].observation;
|
|
91
|
+
thought = "Gerekli aramaları ve analizleri tamamladım. Sonucu kullanıcıya sunuyorum.";
|
|
92
|
+
finished = true;
|
|
93
|
+
finalAnswer = `Otonom ReAct Görevi Başarıyla Tamamlandı.\n` +
|
|
94
|
+
`- Hafıza Kaydı: ${logs[0].observation}\n` +
|
|
95
|
+
`- Morfolojik Yapı: ${prevObs}`;
|
|
96
|
+
}
|
|
97
|
+
} else if (lowerGoal.includes("hesapla") || /[0-9+\-*/%^]/.test(lowerGoal)) {
|
|
98
|
+
if (step === 1) {
|
|
99
|
+
thought = "Matematiksel ifadeyi deterministik parser ile hesaplamalıyım.";
|
|
100
|
+
action = "math_eval";
|
|
101
|
+
// extract arithmetic expression
|
|
102
|
+
const match = goal.match(/[0-9+\-*/%^().\s]+/);
|
|
103
|
+
actionArg = match ? match[0].trim() : "0";
|
|
104
|
+
} else if (step === 2) {
|
|
105
|
+
const val = logs[0].observation;
|
|
106
|
+
thought = `Hesaplanan '${val}' sonucunun spektral enerji yoğunluğunu kontrol etmeliyim.`;
|
|
107
|
+
action = "spectral_transform";
|
|
108
|
+
actionArg = val;
|
|
109
|
+
} else {
|
|
110
|
+
thought = "Hesaplama ve spektral dönüşüm adımları bitti. Sonucu dönüyorum.";
|
|
111
|
+
finished = true;
|
|
112
|
+
finalAnswer = `Hesaplama Sonucu: ${logs[0].observation} | Spektral Temsiliyet: ${logs[1].observation}`;
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
// Generic default task path
|
|
116
|
+
if (step === 1) {
|
|
117
|
+
thought = "Sistem saatini alarak güncel zamanı kontrol etmeliyim.";
|
|
118
|
+
action = "system_time";
|
|
119
|
+
actionArg = "";
|
|
120
|
+
} else {
|
|
121
|
+
thought = "Varsayılan otonom akış tamamlandı.";
|
|
122
|
+
finished = true;
|
|
123
|
+
finalAnswer = `Mevcut Zaman Dilimi: ${logs[0].observation}`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!finished) {
|
|
128
|
+
// Execute tool action
|
|
129
|
+
let observation = "";
|
|
130
|
+
const toolFn = this.tools[action];
|
|
131
|
+
if (toolFn) {
|
|
132
|
+
observation = toolFn(actionArg);
|
|
133
|
+
} else {
|
|
134
|
+
observation = `Hata: '${action}' aracı tanımlı değil.`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
logs.push({
|
|
138
|
+
step,
|
|
139
|
+
thought,
|
|
140
|
+
action,
|
|
141
|
+
argument: actionArg,
|
|
142
|
+
observation
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
step++;
|
|
146
|
+
} else {
|
|
147
|
+
logs.push({
|
|
148
|
+
step,
|
|
149
|
+
thought,
|
|
150
|
+
action: "final_answer",
|
|
151
|
+
argument: "",
|
|
152
|
+
observation: finalAnswer
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
goal,
|
|
159
|
+
stepsRun: step - 1,
|
|
160
|
+
logs,
|
|
161
|
+
finalAnswer
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
package/core/spectral.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectral Computing Engine
|
|
3
|
+
* Implements Radix-2 Cooley-Tukey FFT / IFFT and spectral feature analysis.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Representation } from './hdc.js';
|
|
7
|
+
|
|
8
|
+
export class SpectralEngine {
|
|
9
|
+
constructor() {}
|
|
10
|
+
|
|
11
|
+
// ── FFT CORE ALGORITHMS ────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
_bitReverse(re, im) {
|
|
14
|
+
const n = re.length;
|
|
15
|
+
let j = 0;
|
|
16
|
+
for (let i = 0; i < n; i++) {
|
|
17
|
+
if (i < j) {
|
|
18
|
+
let temp = re[i]; re[i] = re[j]; re[j] = temp;
|
|
19
|
+
temp = im[i]; im[i] = im[j]; im[j] = temp;
|
|
20
|
+
}
|
|
21
|
+
let m = n >> 1;
|
|
22
|
+
while (m >= 2 && j >= m) {
|
|
23
|
+
j -= m;
|
|
24
|
+
m >>= 1;
|
|
25
|
+
}
|
|
26
|
+
j += m;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_fftCore(re, im, inverse = false) {
|
|
31
|
+
const n = re.length;
|
|
32
|
+
this._bitReverse(re, im);
|
|
33
|
+
|
|
34
|
+
for (let len = 2; len <= n; len <<= 1) {
|
|
35
|
+
const angle = (2 * Math.PI / len) * (inverse ? 1 : -1);
|
|
36
|
+
const wlen_re = Math.cos(angle);
|
|
37
|
+
const wlen_im = Math.sin(angle);
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < n; i += len) {
|
|
40
|
+
let w_re = 1.0;
|
|
41
|
+
let w_im = 0.0;
|
|
42
|
+
const half = len >> 1;
|
|
43
|
+
|
|
44
|
+
for (let j = 0; j < half; j++) {
|
|
45
|
+
const u_re = re[i + j];
|
|
46
|
+
const u_im = im[i + j];
|
|
47
|
+
|
|
48
|
+
const t_re = re[i + j + half];
|
|
49
|
+
const t_im = im[i + j + half];
|
|
50
|
+
|
|
51
|
+
const v_re = t_re * w_re - t_im * w_im;
|
|
52
|
+
const v_im = t_re * w_im + t_im * w_re;
|
|
53
|
+
|
|
54
|
+
re[i + j] = u_re + v_re;
|
|
55
|
+
im[i + j] = u_im + v_im;
|
|
56
|
+
|
|
57
|
+
re[i + j + half] = u_re - v_re;
|
|
58
|
+
im[i + j + half] = u_im - v_im;
|
|
59
|
+
|
|
60
|
+
const next_w_re = w_re * wlen_re - w_im * wlen_im;
|
|
61
|
+
const next_w_im = w_re * wlen_im + w_im * wlen_re;
|
|
62
|
+
w_re = next_w_re;
|
|
63
|
+
w_im = next_w_im;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (inverse) {
|
|
69
|
+
for (let i = 0; i < n; i++) {
|
|
70
|
+
re[i] /= n;
|
|
71
|
+
im[i] /= n;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── EXTERNAL API ───────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
spectralTransform(representation) {
|
|
79
|
+
const D = representation.D;
|
|
80
|
+
const re = new Float32Array(D);
|
|
81
|
+
const im = new Float32Array(D);
|
|
82
|
+
|
|
83
|
+
// Map different representation formats into complex domain
|
|
84
|
+
if (representation.type === 'complex') {
|
|
85
|
+
// Phase-coded: z = e^{i * theta}
|
|
86
|
+
for (let i = 0; i < D; i++) {
|
|
87
|
+
re[i] = Math.cos(representation.values[i]);
|
|
88
|
+
im[i] = Math.sin(representation.values[i]);
|
|
89
|
+
}
|
|
90
|
+
} else if (representation.type === 'bipolar' || representation.type === 'real') {
|
|
91
|
+
// Real-valued: z = x + 0i
|
|
92
|
+
for (let i = 0; i < D; i++) {
|
|
93
|
+
re[i] = representation.values[i];
|
|
94
|
+
im[i] = 0.0;
|
|
95
|
+
}
|
|
96
|
+
} else if (representation.type === 'binary') {
|
|
97
|
+
// Map 0 -> -1, 1 -> 1
|
|
98
|
+
for (let i = 0; i < D; i++) {
|
|
99
|
+
re[i] = representation.values[i] === 1 ? 1.0 : -1.0;
|
|
100
|
+
im[i] = 0.0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Run FFT in-place
|
|
105
|
+
this._fftCore(re, im, false);
|
|
106
|
+
|
|
107
|
+
// Calculate magnitude and phase spectrum
|
|
108
|
+
const magnitude = new Float32Array(D);
|
|
109
|
+
const phase = new Float32Array(D);
|
|
110
|
+
for (let i = 0; i < D; i++) {
|
|
111
|
+
magnitude[i] = Math.sqrt(re[i] * re[i] + im[i] * im[i]);
|
|
112
|
+
phase[i] = Math.atan2(im[i], re[i]);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
type: representation.type,
|
|
117
|
+
D,
|
|
118
|
+
re,
|
|
119
|
+
im,
|
|
120
|
+
magnitude,
|
|
121
|
+
phase
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
inverseSpectralTransform(spectrum) {
|
|
126
|
+
const D = spectrum.D;
|
|
127
|
+
const re = new Float32Array(spectrum.re);
|
|
128
|
+
const im = new Float32Array(spectrum.im);
|
|
129
|
+
|
|
130
|
+
// Run IFFT in-place
|
|
131
|
+
this._fftCore(re, im, true);
|
|
132
|
+
|
|
133
|
+
// Reconstruct the original representation type
|
|
134
|
+
if (spectrum.type === 'complex') {
|
|
135
|
+
const vals = new Float32Array(D);
|
|
136
|
+
for (let i = 0; i < D; i++) {
|
|
137
|
+
let v = Math.atan2(im[i], re[i]);
|
|
138
|
+
if (v < 0) v += 2 * Math.PI;
|
|
139
|
+
vals[i] = v;
|
|
140
|
+
}
|
|
141
|
+
return new Representation('complex', vals, D);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (spectrum.type === 'binary') {
|
|
145
|
+
const vals = new Uint8Array(D);
|
|
146
|
+
for (let i = 0; i < D; i++) {
|
|
147
|
+
vals[i] = re[i] >= 0.0 ? 1 : 0;
|
|
148
|
+
}
|
|
149
|
+
return new Representation('binary', vals, D);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (spectrum.type === 'bipolar') {
|
|
153
|
+
const vals = new Float32Array(D);
|
|
154
|
+
for (let i = 0; i < D; i++) {
|
|
155
|
+
vals[i] = re[i] >= 0.0 ? 1.0 : -1.0;
|
|
156
|
+
}
|
|
157
|
+
return new Representation('bipolar', vals, D);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Real representation fallback
|
|
161
|
+
const vals = new Float32Array(D);
|
|
162
|
+
for (let i = 0; i < D; i++) {
|
|
163
|
+
vals[i] = re[i];
|
|
164
|
+
}
|
|
165
|
+
return new Representation('real', vals, D);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
extractDominantFrequencies(spectrum, k = 10) {
|
|
169
|
+
const n = spectrum.D;
|
|
170
|
+
const items = [];
|
|
171
|
+
for (let i = 0; i < n; i++) {
|
|
172
|
+
items.push({ freq: i, mag: spectrum.magnitude[i] });
|
|
173
|
+
}
|
|
174
|
+
// Sort descending by magnitude
|
|
175
|
+
items.sort((a, b) => b.mag - a.mag);
|
|
176
|
+
return items.slice(0, k);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
spectralEnergy(spectrum) {
|
|
180
|
+
let energy = 0;
|
|
181
|
+
const n = spectrum.D;
|
|
182
|
+
for (let i = 0; i < n; i++) {
|
|
183
|
+
energy += spectrum.magnitude[i] * spectrum.magnitude[i];
|
|
184
|
+
}
|
|
185
|
+
return energy;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
spectralEntropy(spectrum) {
|
|
189
|
+
const n = spectrum.D;
|
|
190
|
+
const power = new Float32Array(n);
|
|
191
|
+
let totalPower = 0;
|
|
192
|
+
for (let i = 0; i < n; i++) {
|
|
193
|
+
power[i] = spectrum.magnitude[i] * spectrum.magnitude[i];
|
|
194
|
+
totalPower += power[i];
|
|
195
|
+
}
|
|
196
|
+
if (totalPower === 0) return 0.0;
|
|
197
|
+
|
|
198
|
+
let entropy = 0.0;
|
|
199
|
+
for (let i = 0; i < n; i++) {
|
|
200
|
+
const p = power[i] / totalPower;
|
|
201
|
+
if (p > 0) {
|
|
202
|
+
entropy -= p * Math.log2(p);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Normalized by log2(n)
|
|
206
|
+
return entropy / Math.log2(n);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
spectralSparsity(spectrum) {
|
|
210
|
+
// Hoyer sparsity measure: (sqrt(n) - L1/L2) / (sqrt(n) - 1)
|
|
211
|
+
const n = spectrum.D;
|
|
212
|
+
let l1 = 0;
|
|
213
|
+
let l2Sq = 0;
|
|
214
|
+
for (let i = 0; i < n; i++) {
|
|
215
|
+
l1 += spectrum.magnitude[i];
|
|
216
|
+
l2Sq += spectrum.magnitude[i] * spectrum.magnitude[i];
|
|
217
|
+
}
|
|
218
|
+
const l2 = Math.sqrt(l2Sq);
|
|
219
|
+
if (l2 === 0) return 0.0;
|
|
220
|
+
|
|
221
|
+
const sqrtN = Math.sqrt(n);
|
|
222
|
+
return (sqrtN - l1 / l2) / (sqrtN - 1.0);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
lowFrequencyEnergy(spectrum) {
|
|
226
|
+
// Energy in the lower half of spectrum frequencies
|
|
227
|
+
let energy = 0;
|
|
228
|
+
const half = Math.floor(spectrum.D / 2);
|
|
229
|
+
const quarter = Math.floor(half / 2);
|
|
230
|
+
for (let i = 0; i < quarter; i++) {
|
|
231
|
+
energy += spectrum.magnitude[i] * spectrum.magnitude[i];
|
|
232
|
+
}
|
|
233
|
+
return energy;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
highFrequencyEnergy(spectrum) {
|
|
237
|
+
let energy = 0;
|
|
238
|
+
const half = Math.floor(spectrum.D / 2);
|
|
239
|
+
const quarter = Math.floor(half / 2);
|
|
240
|
+
for (let i = quarter; i < half; i++) {
|
|
241
|
+
energy += spectrum.magnitude[i] * spectrum.magnitude[i];
|
|
242
|
+
}
|
|
243
|
+
return energy;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
spectralSimilarity(a, b) {
|
|
247
|
+
if (a.D !== b.D) return 0.0;
|
|
248
|
+
// Cosine similarity of magnitude spectra
|
|
249
|
+
let dot = 0, normA = 0, normB = 0;
|
|
250
|
+
const n = a.D;
|
|
251
|
+
for (let i = 0; i < n; i++) {
|
|
252
|
+
dot += a.magnitude[i] * b.magnitude[i];
|
|
253
|
+
normA += a.magnitude[i] * a.magnitude[i];
|
|
254
|
+
normB += b.magnitude[i] * b.magnitude[i];
|
|
255
|
+
}
|
|
256
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
257
|
+
return denom > 0 ? dot / denom : 0.0;
|
|
258
|
+
}
|
|
259
|
+
}
|