@aleph-ai/tinyaleph 1.3.0 → 1.4.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 +423 -12
- package/backends/cryptographic/index.js +455 -2
- package/core/beacon.js +735 -0
- package/core/crt-homology.js +1004 -0
- package/core/enochian-vocabulary.js +910 -0
- package/core/enochian.js +744 -0
- package/core/errors.js +587 -0
- package/core/hilbert.js +651 -1
- package/core/index.js +86 -1
- package/core/lambda.js +284 -33
- package/core/logger.js +350 -0
- package/core/prime.js +136 -1
- package/core/quaternion-semantics.js +623 -0
- package/core/reduction.js +391 -1
- package/core/rformer-crt.js +892 -0
- package/core/topology.js +655 -0
- package/docs/README.md +54 -0
- package/docs/reference/07-topology.md +257 -0
- package/docs/reference/08-observer.md +421 -0
- package/docs/reference/09-crt-homology.md +369 -0
- package/modular.js +231 -3
- package/package.json +1 -1
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quaternionic Semantic Embedding (PIQC.pdf §3)
|
|
3
|
+
*
|
|
4
|
+
* Prime-indexed quaternionic semantics for directional meaning:
|
|
5
|
+
* - Ψ(p) = cos(h(p)) + u(p)sin(h(p)) prime-to-quaternion mapping
|
|
6
|
+
* - Q(A(p1)...N(q)) = Ψ(p1) ⊗ ... ⊗ Ψ(q) chain composition
|
|
7
|
+
* - Configurable axis mapping based on prime number-theoretic properties
|
|
8
|
+
*
|
|
9
|
+
* The semantic axes are NOT hardcoded with meanings. Instead, the axes
|
|
10
|
+
* emerge from the prime's number-theoretic properties:
|
|
11
|
+
* - Quadratic residue character (mod 4, mod 8)
|
|
12
|
+
* - Position in prime sequence
|
|
13
|
+
* - Relationship to golden ratio φ
|
|
14
|
+
*
|
|
15
|
+
* Meaning emerges from the geometric relationships between primes,
|
|
16
|
+
* not from predetermined labels.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { isPrime, twistAngle, nthPrime } = require('./prime');
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// AXIS MAPPING STRATEGIES
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Golden ratio for axis distribution
|
|
27
|
+
*/
|
|
28
|
+
const PHI = (1 + Math.sqrt(5)) / 2;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Base class for axis mapping strategies
|
|
32
|
+
*/
|
|
33
|
+
class AxisMapper {
|
|
34
|
+
/**
|
|
35
|
+
* Map a prime to an axis vector {i, j, k}
|
|
36
|
+
* @param {number} p - Prime number
|
|
37
|
+
* @returns {{i: number, j: number, k: number}} Unit vector
|
|
38
|
+
*/
|
|
39
|
+
map(p) {
|
|
40
|
+
throw new Error('Subclass must implement map()');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Modular axis mapper - uses quadratic residue properties
|
|
46
|
+
*
|
|
47
|
+
* Maps primes to axes based on their behavior mod small numbers:
|
|
48
|
+
* - p mod 4 determines i-component sign
|
|
49
|
+
* - p mod 8 determines j-component weight
|
|
50
|
+
* - p mod 6 determines k-component weight
|
|
51
|
+
*
|
|
52
|
+
* This is purely number-theoretic with no semantic labels.
|
|
53
|
+
*/
|
|
54
|
+
class ModularAxisMapper extends AxisMapper {
|
|
55
|
+
map(p) {
|
|
56
|
+
// Use quadratic residue character for i-axis
|
|
57
|
+
// p ≡ 1 (mod 4) means p = a² + b² (sum of two squares)
|
|
58
|
+
const mod4 = p % 4;
|
|
59
|
+
const i = mod4 === 1 ? 1 : mod4 === 3 ? -1 : 0;
|
|
60
|
+
|
|
61
|
+
// Use mod 8 for j-axis (determines if p = a² + 2b²)
|
|
62
|
+
const mod8 = p % 8;
|
|
63
|
+
const j = (mod8 === 1 || mod8 === 3) ? 1 : (mod8 === 5 || mod8 === 7) ? -1 : 0;
|
|
64
|
+
|
|
65
|
+
// Use mod 6 for k-axis (relates to cubic residues)
|
|
66
|
+
const mod6 = p % 6;
|
|
67
|
+
const k = mod6 === 1 ? 1 : mod6 === 5 ? -1 : 0;
|
|
68
|
+
|
|
69
|
+
// Normalize to unit vector
|
|
70
|
+
const len = Math.sqrt(i*i + j*j + k*k);
|
|
71
|
+
if (len < 1e-10) {
|
|
72
|
+
// p = 2 or p = 3: return special axis
|
|
73
|
+
return p === 2 ? { i: 1, j: 0, k: 0 } : { i: 0, j: 1, k: 0 };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { i: i/len, j: j/len, k: k/len };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Golden ratio axis mapper - uses golden angle distribution
|
|
82
|
+
*
|
|
83
|
+
* Maps each prime to a point on the unit sphere using the golden angle,
|
|
84
|
+
* creating maximally uniform distribution (like sunflower seeds).
|
|
85
|
+
*/
|
|
86
|
+
class GoldenAxisMapper extends AxisMapper {
|
|
87
|
+
constructor() {
|
|
88
|
+
super();
|
|
89
|
+
this.primeIndex = new Map();
|
|
90
|
+
this.currentIndex = 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
map(p) {
|
|
94
|
+
// Get or assign index for this prime
|
|
95
|
+
if (!this.primeIndex.has(p)) {
|
|
96
|
+
this.primeIndex.set(p, this.currentIndex++);
|
|
97
|
+
}
|
|
98
|
+
const n = this.primeIndex.get(p);
|
|
99
|
+
|
|
100
|
+
// Golden angle in radians
|
|
101
|
+
const goldenAngle = 2 * Math.PI / (PHI * PHI);
|
|
102
|
+
|
|
103
|
+
// Spherical coordinates using golden ratio
|
|
104
|
+
const theta = goldenAngle * n;
|
|
105
|
+
const phi = Math.acos(1 - 2 * (n + 0.5) / (this.currentIndex + 1));
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
i: Math.sin(phi) * Math.cos(theta),
|
|
109
|
+
j: Math.sin(phi) * Math.sin(theta),
|
|
110
|
+
k: Math.cos(phi)
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Twist angle axis mapper - uses κ(p) = 360°/p to distribute axes
|
|
117
|
+
*
|
|
118
|
+
* The twist angle naturally encodes the prime's "rotation frequency"
|
|
119
|
+
* and creates a geometrically meaningful distribution.
|
|
120
|
+
*/
|
|
121
|
+
class TwistAxisMapper extends AxisMapper {
|
|
122
|
+
map(p) {
|
|
123
|
+
// Use twist angle for spherical position
|
|
124
|
+
const kappa = twistAngle(p) * Math.PI / 180; // to radians
|
|
125
|
+
|
|
126
|
+
// Use log(p) for elevation angle (larger primes → lower elevation)
|
|
127
|
+
const elevation = Math.PI / 2 * (1 - Math.log(p) / Math.log(1000));
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
i: Math.cos(kappa) * Math.cos(elevation),
|
|
131
|
+
j: Math.sin(kappa) * Math.cos(elevation),
|
|
132
|
+
k: Math.sin(elevation)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Custom axis mapper - user provides the mapping function
|
|
139
|
+
*/
|
|
140
|
+
class CustomAxisMapper extends AxisMapper {
|
|
141
|
+
constructor(mapFn) {
|
|
142
|
+
super();
|
|
143
|
+
this.mapFn = mapFn;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
map(p) {
|
|
147
|
+
const axis = this.mapFn(p);
|
|
148
|
+
// Normalize
|
|
149
|
+
const len = Math.sqrt(axis.i ** 2 + axis.j ** 2 + axis.k ** 2);
|
|
150
|
+
if (len < 1e-10) return { i: 1, j: 0, k: 0 };
|
|
151
|
+
return { i: axis.i / len, j: axis.j / len, k: axis.k / len };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Default mapper uses modular arithmetic (purely number-theoretic)
|
|
156
|
+
let defaultMapper = new ModularAxisMapper();
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get the current axis mapper
|
|
160
|
+
*/
|
|
161
|
+
function getAxisMapper() {
|
|
162
|
+
return defaultMapper;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Set a new axis mapper
|
|
167
|
+
* @param {AxisMapper} mapper - New mapper to use
|
|
168
|
+
*/
|
|
169
|
+
function setAxisMapper(mapper) {
|
|
170
|
+
if (!(mapper instanceof AxisMapper)) {
|
|
171
|
+
throw new TypeError('Mapper must be instance of AxisMapper');
|
|
172
|
+
}
|
|
173
|
+
defaultMapper = mapper;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Prime → axis mapping using current mapper
|
|
178
|
+
* @param {number} p - Prime number
|
|
179
|
+
* @returns {{i: number, j: number, k: number}} Unit axis vector
|
|
180
|
+
*/
|
|
181
|
+
function primeToAxis(p) {
|
|
182
|
+
return defaultMapper.map(p);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ============================================================================
|
|
186
|
+
// UNIT QUATERNION CLASS
|
|
187
|
+
// ============================================================================
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* UnitQuaternion - Quaternion with unit norm for rotations
|
|
191
|
+
* q = a + bi + cj + dk where |q| = 1
|
|
192
|
+
*/
|
|
193
|
+
class UnitQuaternion {
|
|
194
|
+
constructor(a = 1, b = 0, c = 0, d = 0) {
|
|
195
|
+
this.a = a; // Real/scalar part
|
|
196
|
+
this.b = b; // i component
|
|
197
|
+
this.c = c; // j component
|
|
198
|
+
this.d = d; // k component
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Create unit quaternion from angle-axis representation
|
|
203
|
+
* q = cos(θ/2) + sin(θ/2)(xi + yj + zk)
|
|
204
|
+
*
|
|
205
|
+
* @param {number} theta - Rotation angle in radians
|
|
206
|
+
* @param {Object} axis - Axis vector {i, j, k}
|
|
207
|
+
*/
|
|
208
|
+
static fromAxisAngle(theta, axis) {
|
|
209
|
+
const halfTheta = theta / 2;
|
|
210
|
+
const sinHalf = Math.sin(halfTheta);
|
|
211
|
+
const cosHalf = Math.cos(halfTheta);
|
|
212
|
+
|
|
213
|
+
// Normalize axis
|
|
214
|
+
const len = Math.sqrt(axis.i ** 2 + axis.j ** 2 + axis.k ** 2);
|
|
215
|
+
const norm = len > 1e-10 ? len : 1;
|
|
216
|
+
|
|
217
|
+
return new UnitQuaternion(
|
|
218
|
+
cosHalf,
|
|
219
|
+
sinHalf * axis.i / norm,
|
|
220
|
+
sinHalf * axis.j / norm,
|
|
221
|
+
sinHalf * axis.k / norm
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create identity quaternion (1 + 0i + 0j + 0k)
|
|
227
|
+
*/
|
|
228
|
+
static identity() {
|
|
229
|
+
return new UnitQuaternion(1, 0, 0, 0);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Hamilton product: q₁ ⊗ q₂ (non-commutative!)
|
|
234
|
+
*/
|
|
235
|
+
multiply(other) {
|
|
236
|
+
return new UnitQuaternion(
|
|
237
|
+
this.a * other.a - this.b * other.b - this.c * other.c - this.d * other.d,
|
|
238
|
+
this.a * other.b + this.b * other.a + this.c * other.d - this.d * other.c,
|
|
239
|
+
this.a * other.c - this.b * other.d + this.c * other.a + this.d * other.b,
|
|
240
|
+
this.a * other.d + this.b * other.c - this.c * other.b + this.d * other.a
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Quaternion conjugate: q* = a - bi - cj - dk
|
|
246
|
+
*/
|
|
247
|
+
conjugate() {
|
|
248
|
+
return new UnitQuaternion(this.a, -this.b, -this.c, -this.d);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Quaternion norm: |q|
|
|
253
|
+
*/
|
|
254
|
+
norm() {
|
|
255
|
+
return Math.sqrt(this.a ** 2 + this.b ** 2 + this.c ** 2 + this.d ** 2);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Normalize to unit quaternion
|
|
260
|
+
*/
|
|
261
|
+
normalize() {
|
|
262
|
+
const n = this.norm();
|
|
263
|
+
if (n < 1e-10) return UnitQuaternion.identity();
|
|
264
|
+
return new UnitQuaternion(
|
|
265
|
+
this.a / n, this.b / n, this.c / n, this.d / n
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Inner product with another quaternion
|
|
271
|
+
*/
|
|
272
|
+
dot(other) {
|
|
273
|
+
return this.a * other.a + this.b * other.b + this.c * other.c + this.d * other.d;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Extract rotation angle (radians)
|
|
278
|
+
*/
|
|
279
|
+
angle() {
|
|
280
|
+
return 2 * Math.acos(Math.min(1, Math.max(-1, this.a)));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Extract rotation axis
|
|
285
|
+
*/
|
|
286
|
+
axis() {
|
|
287
|
+
const sinHalf = Math.sqrt(1 - this.a * this.a);
|
|
288
|
+
if (sinHalf < 1e-10) {
|
|
289
|
+
return { i: 0, j: 0, k: 1 }; // Default axis
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
i: this.b / sinHalf,
|
|
293
|
+
j: this.c / sinHalf,
|
|
294
|
+
k: this.d / sinHalf
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Spherical linear interpolation (slerp)
|
|
300
|
+
*/
|
|
301
|
+
slerp(other, t) {
|
|
302
|
+
let cosTheta = this.dot(other);
|
|
303
|
+
|
|
304
|
+
// Handle negative dot product (take shorter path)
|
|
305
|
+
let target = other;
|
|
306
|
+
if (cosTheta < 0) {
|
|
307
|
+
cosTheta = -cosTheta;
|
|
308
|
+
target = new UnitQuaternion(-other.a, -other.b, -other.c, -other.d);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (cosTheta > 0.9995) {
|
|
312
|
+
// Linear interpolation for nearly parallel quaternions
|
|
313
|
+
return new UnitQuaternion(
|
|
314
|
+
this.a + t * (target.a - this.a),
|
|
315
|
+
this.b + t * (target.b - this.b),
|
|
316
|
+
this.c + t * (target.c - this.c),
|
|
317
|
+
this.d + t * (target.d - this.d)
|
|
318
|
+
).normalize();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const theta = Math.acos(cosTheta);
|
|
322
|
+
const sinTheta = Math.sin(theta);
|
|
323
|
+
const s0 = Math.sin((1 - t) * theta) / sinTheta;
|
|
324
|
+
const s1 = Math.sin(t * theta) / sinTheta;
|
|
325
|
+
|
|
326
|
+
return new UnitQuaternion(
|
|
327
|
+
s0 * this.a + s1 * target.a,
|
|
328
|
+
s0 * this.b + s1 * target.b,
|
|
329
|
+
s0 * this.c + s1 * target.c,
|
|
330
|
+
s0 * this.d + s1 * target.d
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Convert to array [a, b, c, d]
|
|
336
|
+
*/
|
|
337
|
+
toArray() {
|
|
338
|
+
return [this.a, this.b, this.c, this.d];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Clone the quaternion
|
|
343
|
+
*/
|
|
344
|
+
clone() {
|
|
345
|
+
return new UnitQuaternion(this.a, this.b, this.c, this.d);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
toString() {
|
|
349
|
+
const parts = [];
|
|
350
|
+
if (Math.abs(this.a) > 1e-10) parts.push(this.a.toFixed(4));
|
|
351
|
+
if (Math.abs(this.b) > 1e-10) parts.push(`${this.b.toFixed(4)}i`);
|
|
352
|
+
if (Math.abs(this.c) > 1e-10) parts.push(`${this.c.toFixed(4)}j`);
|
|
353
|
+
if (Math.abs(this.d) > 1e-10) parts.push(`${this.d.toFixed(4)}k`);
|
|
354
|
+
return parts.length > 0 ? parts.join(' + ').replace(/\+ -/g, '- ') : '0';
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ============================================================================
|
|
359
|
+
// PRIME-TO-QUATERNION MAPPING Ψ(p)
|
|
360
|
+
// ============================================================================
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Prime-to-quaternion mapping functions
|
|
364
|
+
*
|
|
365
|
+
* From PIQC §3.1:
|
|
366
|
+
* Ψ(p) = cos(h(p)) + u(p)sin(h(p))
|
|
367
|
+
*
|
|
368
|
+
* Where:
|
|
369
|
+
* - h(p) is the half-angle function
|
|
370
|
+
* - u(p) is the unit axis function
|
|
371
|
+
*/
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Half-angle function h(p)
|
|
375
|
+
* Maps prime to rotation half-angle
|
|
376
|
+
*/
|
|
377
|
+
function halfAngle(p) {
|
|
378
|
+
// Use twist angle κ(p) = 360°/p as the rotation angle
|
|
379
|
+
// h(p) = κ(p)/2 in radians
|
|
380
|
+
const kappaRad = (twistAngle(p) * Math.PI) / 180;
|
|
381
|
+
return kappaRad / 2;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Axis function u(p)
|
|
386
|
+
* Maps prime to rotation axis based on semantic category
|
|
387
|
+
*/
|
|
388
|
+
function axisFunction(p) {
|
|
389
|
+
return primeToAxis(p);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Prime-to-quaternion map Ψ(p)
|
|
394
|
+
*
|
|
395
|
+
* @param {number} p - Prime number
|
|
396
|
+
* @returns {UnitQuaternion} Unit quaternion for this prime
|
|
397
|
+
*/
|
|
398
|
+
function Psi(p) {
|
|
399
|
+
if (!isPrime(p)) {
|
|
400
|
+
throw new Error(`Psi requires prime, got ${p}`);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const h = halfAngle(p);
|
|
404
|
+
const u = axisFunction(p);
|
|
405
|
+
|
|
406
|
+
return UnitQuaternion.fromAxisAngle(2 * h, u);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ============================================================================
|
|
410
|
+
// QUATERNIONIC CHAIN COMPOSITION
|
|
411
|
+
// ============================================================================
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Compute quaternionic embedding of an operator chain
|
|
415
|
+
*
|
|
416
|
+
* Q(A(p₁)...A(pₖ)N(q)) = Ψ(p₁) ⊗ ... ⊗ Ψ(pₖ) ⊗ Ψ(q)
|
|
417
|
+
*
|
|
418
|
+
* @param {number[]} operatorPrimes - Array of operator primes [p₁, ..., pₖ]
|
|
419
|
+
* @param {number} nounPrime - Noun prime q
|
|
420
|
+
* @returns {UnitQuaternion} Composed quaternion
|
|
421
|
+
*/
|
|
422
|
+
function quaternionicChain(operatorPrimes, nounPrime) {
|
|
423
|
+
// Start with identity
|
|
424
|
+
let result = UnitQuaternion.identity();
|
|
425
|
+
|
|
426
|
+
// Apply operators left-to-right (Hamilton product is associative)
|
|
427
|
+
for (const p of operatorPrimes) {
|
|
428
|
+
result = result.multiply(Psi(p));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Apply noun quaternion
|
|
432
|
+
result = result.multiply(Psi(nounPrime));
|
|
433
|
+
|
|
434
|
+
return result;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Verify non-commutativity of quaternionic composition
|
|
439
|
+
*
|
|
440
|
+
* @param {number} p1 - First prime
|
|
441
|
+
* @param {number} p2 - Second prime
|
|
442
|
+
* @returns {Object} Comparison of Ψ(p₁)⊗Ψ(p₂) vs Ψ(p₂)⊗Ψ(p₁)
|
|
443
|
+
*/
|
|
444
|
+
function verifyNonCommutativity(p1, p2) {
|
|
445
|
+
const psi1 = Psi(p1);
|
|
446
|
+
const psi2 = Psi(p2);
|
|
447
|
+
|
|
448
|
+
const forward = psi1.multiply(psi2);
|
|
449
|
+
const backward = psi2.multiply(psi1);
|
|
450
|
+
|
|
451
|
+
const difference = Math.sqrt(
|
|
452
|
+
(forward.a - backward.a) ** 2 +
|
|
453
|
+
(forward.b - backward.b) ** 2 +
|
|
454
|
+
(forward.c - backward.c) ** 2 +
|
|
455
|
+
(forward.d - backward.d) ** 2
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
p1, p2,
|
|
460
|
+
forward: forward.toArray(),
|
|
461
|
+
backward: backward.toArray(),
|
|
462
|
+
difference,
|
|
463
|
+
isCommutative: difference < 1e-10
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ============================================================================
|
|
468
|
+
// SEMANTIC COHERENCE METRICS
|
|
469
|
+
// ============================================================================
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Compute semantic coherence between two quaternionic states
|
|
473
|
+
*
|
|
474
|
+
* Coherence = |⟨q₁, q₂⟩|² = (q₁ · q₂)²
|
|
475
|
+
*
|
|
476
|
+
* @param {UnitQuaternion} q1 - First quaternion
|
|
477
|
+
* @param {UnitQuaternion} q2 - Second quaternion
|
|
478
|
+
* @returns {number} Coherence in [0, 1]
|
|
479
|
+
*/
|
|
480
|
+
function semanticCoherence(q1, q2) {
|
|
481
|
+
const dot = q1.dot(q2);
|
|
482
|
+
return dot * dot;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Extract axis projections from a quaternion
|
|
487
|
+
*
|
|
488
|
+
* Returns the normalized i, j, k components without semantic labels.
|
|
489
|
+
* The meaning of these axes depends on the AxisMapper in use.
|
|
490
|
+
*
|
|
491
|
+
* @param {UnitQuaternion} q - Quaternion to analyze
|
|
492
|
+
* @returns {Object} Projections onto i, j, k axes
|
|
493
|
+
*/
|
|
494
|
+
function axisProjections(q) {
|
|
495
|
+
// The i, j, k components - meaning depends on mapper configuration
|
|
496
|
+
const norm = Math.sqrt(q.b ** 2 + q.c ** 2 + q.d ** 2);
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
i: norm > 1e-10 ? q.b / norm : 0,
|
|
500
|
+
j: norm > 1e-10 ? q.c / norm : 0,
|
|
501
|
+
k: norm > 1e-10 ? q.d / norm : 0,
|
|
502
|
+
rotationAngle: q.angle() * 180 / Math.PI, // degrees
|
|
503
|
+
purity: norm // How much of the quaternion is "rotation" vs "identity"
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Compute semantic distance between chains
|
|
509
|
+
*
|
|
510
|
+
* @param {number[]} ops1 - First chain operators
|
|
511
|
+
* @param {number} noun1 - First chain noun
|
|
512
|
+
* @param {number[]} ops2 - Second chain operators
|
|
513
|
+
* @param {number} noun2 - Second chain noun
|
|
514
|
+
* @returns {number} Geodesic distance on quaternion sphere
|
|
515
|
+
*/
|
|
516
|
+
function chainDistance(ops1, noun1, ops2, noun2) {
|
|
517
|
+
const q1 = quaternionicChain(ops1, noun1);
|
|
518
|
+
const q2 = quaternionicChain(ops2, noun2);
|
|
519
|
+
|
|
520
|
+
const dot = Math.abs(q1.dot(q2));
|
|
521
|
+
const angle = 2 * Math.acos(Math.min(1, dot));
|
|
522
|
+
|
|
523
|
+
return angle;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// ============================================================================
|
|
527
|
+
// PRIME FAMILY ANALYSIS
|
|
528
|
+
// ============================================================================
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Analyze quaternionic properties of a prime family
|
|
532
|
+
*
|
|
533
|
+
* @param {number[]} primes - Array of primes to analyze
|
|
534
|
+
* @returns {Object} Family analysis
|
|
535
|
+
*/
|
|
536
|
+
function analyzePrimeFamily(primes) {
|
|
537
|
+
const quaternions = primes.map(p => ({ prime: p, psi: Psi(p) }));
|
|
538
|
+
|
|
539
|
+
// Compute pairwise coherences
|
|
540
|
+
const coherences = [];
|
|
541
|
+
for (let i = 0; i < primes.length; i++) {
|
|
542
|
+
for (let j = i + 1; j < primes.length; j++) {
|
|
543
|
+
coherences.push({
|
|
544
|
+
p1: primes[i],
|
|
545
|
+
p2: primes[j],
|
|
546
|
+
coherence: semanticCoherence(quaternions[i].psi, quaternions[j].psi)
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Find most coherent pairs
|
|
552
|
+
coherences.sort((a, b) => b.coherence - a.coherence);
|
|
553
|
+
|
|
554
|
+
// Compute centroid (average quaternion)
|
|
555
|
+
let centroid = new UnitQuaternion(0, 0, 0, 0);
|
|
556
|
+
for (const { psi } of quaternions) {
|
|
557
|
+
centroid = new UnitQuaternion(
|
|
558
|
+
centroid.a + psi.a,
|
|
559
|
+
centroid.b + psi.b,
|
|
560
|
+
centroid.c + psi.c,
|
|
561
|
+
centroid.d + psi.d
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
centroid = centroid.normalize();
|
|
565
|
+
|
|
566
|
+
// Compute spread (variance around centroid)
|
|
567
|
+
let spread = 0;
|
|
568
|
+
for (const { psi } of quaternions) {
|
|
569
|
+
const dot = psi.dot(centroid);
|
|
570
|
+
spread += 1 - dot * dot;
|
|
571
|
+
}
|
|
572
|
+
spread /= primes.length;
|
|
573
|
+
|
|
574
|
+
return {
|
|
575
|
+
quaternions: quaternions.map(({ prime, psi }) => ({
|
|
576
|
+
prime,
|
|
577
|
+
quaternion: psi.toArray(),
|
|
578
|
+
angle: psi.angle() * 180 / Math.PI,
|
|
579
|
+
projections: axisProjections(psi)
|
|
580
|
+
})),
|
|
581
|
+
coherences: coherences.slice(0, 10), // Top 10
|
|
582
|
+
centroid: centroid.toArray(),
|
|
583
|
+
spread,
|
|
584
|
+
meanCoherence: coherences.reduce((s, c) => s + c.coherence, 0) / coherences.length
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// ============================================================================
|
|
589
|
+
// EXPORTS
|
|
590
|
+
// ============================================================================
|
|
591
|
+
|
|
592
|
+
module.exports = {
|
|
593
|
+
// Axis mapping strategies
|
|
594
|
+
AxisMapper,
|
|
595
|
+
ModularAxisMapper,
|
|
596
|
+
GoldenAxisMapper,
|
|
597
|
+
TwistAxisMapper,
|
|
598
|
+
CustomAxisMapper,
|
|
599
|
+
getAxisMapper,
|
|
600
|
+
setAxisMapper,
|
|
601
|
+
primeToAxis,
|
|
602
|
+
PHI,
|
|
603
|
+
|
|
604
|
+
// Unit quaternion class
|
|
605
|
+
UnitQuaternion,
|
|
606
|
+
|
|
607
|
+
// Prime-to-quaternion mapping
|
|
608
|
+
halfAngle,
|
|
609
|
+
axisFunction,
|
|
610
|
+
Psi,
|
|
611
|
+
|
|
612
|
+
// Chain composition
|
|
613
|
+
quaternionicChain,
|
|
614
|
+
verifyNonCommutativity,
|
|
615
|
+
|
|
616
|
+
// Coherence metrics (no semantic labels)
|
|
617
|
+
semanticCoherence, // Keep name for compatibility but it's just quaternion coherence
|
|
618
|
+
axisProjections,
|
|
619
|
+
chainDistance,
|
|
620
|
+
|
|
621
|
+
// Analysis
|
|
622
|
+
analyzePrimeFamily
|
|
623
|
+
};
|