hyperspace-sdk-ts 3.1.2 → 3.1.3
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 +59 -4
- package/dist/client.d.ts +20 -2
- package/dist/client.js +420 -99
- package/dist/math.d.ts +4 -0
- package/dist/math.js +150 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# HyperspaceDB TypeScript SDK
|
|
2
2
|
|
|
3
|
-
Official TypeScript client for HyperspaceDB gRPC API v3.1.
|
|
3
|
+
Official TypeScript client for HyperspaceDB gRPC API v3.1.1.
|
|
4
4
|
|
|
5
5
|
Use this SDK for:
|
|
6
6
|
- collection lifecycle management
|
|
@@ -144,9 +144,9 @@ const results = await client.searchText("How to use HyperspaceDB?", 10, "coll",
|
|
|
144
144
|
});
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
### Geometric Filters
|
|
147
|
+
### Geometric Filters
|
|
148
148
|
|
|
149
|
-
HyperspaceDB
|
|
149
|
+
HyperspaceDB introduces advanced spatial filters that run on the engine level:
|
|
150
150
|
|
|
151
151
|
```ts
|
|
152
152
|
// 1. Proximity Search (Ball)
|
|
@@ -323,7 +323,7 @@ const syncedThought = CognitiveMath.contextResonance(thought, globalContext, 0.5
|
|
|
323
323
|
const relation = await client.predictRelation(1, 2);
|
|
324
324
|
```
|
|
325
325
|
|
|
326
|
-
## Implicit Graph Engine (v3.
|
|
326
|
+
## Implicit Graph Engine (v3.1.1)
|
|
327
327
|
|
|
328
328
|
HyperspaceDB treats your vectors as nodes in a dynamic graph. Relationships are inferred from the geometry:
|
|
329
329
|
- **Lorentz / Poincare**: Hierarchy and subsumption (light cones).
|
|
@@ -420,3 +420,58 @@ const vector = await embedder.encode("my text");
|
|
|
420
420
|
All methods reject on transport/protocol errors. Targets gRPC data plane operations.
|
|
421
421
|
For control plane endpoints (`/api/*`), use regular HTTP requests to the server's HTTP port.
|
|
422
422
|
|
|
423
|
+
## Zero-Knowledge Client-Side Encryption (ZK-Privacy)
|
|
424
|
+
|
|
425
|
+
HyperspaceDB v3.1.1 introduces Zero-Knowledge client-side encryption (ZK-Privacy). All private data (vectors, metadata, payloads) are encrypted/obfuscated *before* they leave the client. The database server never sees the raw vectors or plaintext data, ensuring maximum security even in public or untrusted DePIN environments.
|
|
426
|
+
|
|
427
|
+
### Key Features
|
|
428
|
+
1. **Vector Projection**: High-dimensional vectors are projected using a deterministic orthogonal matrix (or Lorentz boost matrix for hyperbolic spaces) generated from the collection key. This preserves distances (L2, Cosine, Lorentz) while hiding the vector coordinates.
|
|
429
|
+
2. **Anisotropic Noise Injection**: Injecting subtle deterministic noise into the vectors to prevent reconstruction attacks.
|
|
430
|
+
3. **Payload Encryption**: Sidecar payloads are encrypted client-side using AES-256-GCM before being sent to the database.
|
|
431
|
+
4. **Metadata Hashing**: Metadata keys and values are obfuscated using HMAC-SHA256.
|
|
432
|
+
|
|
433
|
+
### Usage Example
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
import { HyperspaceClient } from "hyperspace-sdk-ts";
|
|
437
|
+
|
|
438
|
+
async function main() {
|
|
439
|
+
const client = new HyperspaceClient("localhost:50051", "I_LOVE_HYPERSPACEDB");
|
|
440
|
+
|
|
441
|
+
const collection = "encrypted_docs";
|
|
442
|
+
const secretKey = "my-super-secret-key";
|
|
443
|
+
|
|
444
|
+
// Register collection key to enable automatic client-side encryption/decryption
|
|
445
|
+
// noiseSigma defaults to 0.02 (2% anisotropic noise)
|
|
446
|
+
client.registerCollectionKey(collection, secretKey, "cosine", 0.02);
|
|
447
|
+
|
|
448
|
+
// 1. Insert vector (will be projected, noise injected, payload encrypted, metadata hashed)
|
|
449
|
+
await client.insert(
|
|
450
|
+
1,
|
|
451
|
+
[0.1, 0.2, 0.3],
|
|
452
|
+
{ category: "confidential" },
|
|
453
|
+
collection,
|
|
454
|
+
undefined,
|
|
455
|
+
undefined,
|
|
456
|
+
Buffer.from("This is a highly secret document payload", "utf-8")
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
// 2. Search (search vector is projected and noise-injected; results are decrypted locally)
|
|
460
|
+
const results = await client.search([0.1, 0.2, 0.3], 5, collection, {
|
|
461
|
+
// Filters are automatically hashed client-side
|
|
462
|
+
filter: { category: "confidential" }
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
for (const res of results) {
|
|
466
|
+
console.log(`ID: ${res.id}, Distance: ${res.distance}`);
|
|
467
|
+
if (res.payload) {
|
|
468
|
+
console.log(`Decrypted Payload: ${Buffer.from(res.payload).toString("utf-8")}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
client.close();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
main().catch(console.error);
|
|
476
|
+
```
|
|
477
|
+
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as grpc from '@grpc/grpc-js';
|
|
2
2
|
import { DurabilityLevel, EventMessage } from './proto/hyperspace_pb';
|
|
3
3
|
import * as hyperspace_pb from './proto/hyperspace_pb';
|
|
4
|
-
export * as
|
|
4
|
+
export * as CognitiveMathExport from './math';
|
|
5
5
|
export { TribunalContext } from './agents';
|
|
6
6
|
export { DurabilityLevel };
|
|
7
7
|
export type TypedMetadataValue = string | number | boolean;
|
|
@@ -140,12 +140,30 @@ export declare class HyperspaceClient {
|
|
|
140
140
|
private host;
|
|
141
141
|
private apiKey?;
|
|
142
142
|
private userId?;
|
|
143
|
+
embedder?: {
|
|
144
|
+
encode: (text: string) => Promise<number[]> | number[];
|
|
145
|
+
};
|
|
146
|
+
private collectionKeys;
|
|
147
|
+
private encryptionContexts;
|
|
148
|
+
private collectionMetrics;
|
|
149
|
+
private collectionNoiseSigmas;
|
|
150
|
+
private collectionSchemas;
|
|
143
151
|
private static toVectorList;
|
|
144
152
|
private static toProtoMetadataValue;
|
|
145
153
|
private static parseTypedMetadata;
|
|
146
154
|
private toProtoFilter;
|
|
147
155
|
constructor(host?: string, apiKey?: string, userId?: string);
|
|
148
|
-
|
|
156
|
+
registerCollectionKey(collectionName: string, key: string, metric?: string, noiseSigma?: number, schema?: CollectionSchema): void;
|
|
157
|
+
private deriveKeys;
|
|
158
|
+
private encryptPayload;
|
|
159
|
+
private decryptPayload;
|
|
160
|
+
private hashMetadataKey;
|
|
161
|
+
private hashMetadataValue;
|
|
162
|
+
private _getEncryptionContext;
|
|
163
|
+
private _projectSingleBlock;
|
|
164
|
+
private _projectCollectionVector;
|
|
165
|
+
private _encryptFilters;
|
|
166
|
+
createCollection(name: string, schema: CollectionSchema, encryptionKey?: string, noiseSigma?: number): Promise<boolean>;
|
|
149
167
|
deleteCollection(name: string): Promise<boolean>;
|
|
150
168
|
freezeCollection(name: string): Promise<string>;
|
|
151
169
|
unfreezeCollection(name: string): Promise<string>;
|
package/dist/client.js
CHANGED
|
@@ -33,13 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.HyperspaceClient = exports.HyperbolicMath = exports.DurabilityLevel = exports.TribunalContext = exports.
|
|
36
|
+
exports.HyperspaceClient = exports.HyperbolicMath = exports.DurabilityLevel = exports.TribunalContext = exports.CognitiveMathExport = void 0;
|
|
37
37
|
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
38
38
|
const hyperspace_grpc_pb_1 = require("./proto/hyperspace_grpc_pb");
|
|
39
39
|
const hyperspace_pb_1 = require("./proto/hyperspace_pb");
|
|
40
40
|
Object.defineProperty(exports, "DurabilityLevel", { enumerable: true, get: function () { return hyperspace_pb_1.DurabilityLevel; } });
|
|
41
41
|
const hyperspace_pb = __importStar(require("./proto/hyperspace_pb")); // New, for direct access to types
|
|
42
|
-
|
|
42
|
+
const CognitiveMath = __importStar(require("./math"));
|
|
43
|
+
exports.CognitiveMathExport = __importStar(require("./math"));
|
|
43
44
|
var agents_1 = require("./agents");
|
|
44
45
|
Object.defineProperty(exports, "TribunalContext", { enumerable: true, get: function () { return agents_1.TribunalContext; } });
|
|
45
46
|
exports.HyperbolicMath = {
|
|
@@ -292,6 +293,11 @@ class HyperspaceClient {
|
|
|
292
293
|
return pf;
|
|
293
294
|
}
|
|
294
295
|
constructor(host = 'localhost:50051', apiKey, userId) {
|
|
296
|
+
this.collectionKeys = {};
|
|
297
|
+
this.encryptionContexts = {};
|
|
298
|
+
this.collectionMetrics = {};
|
|
299
|
+
this.collectionNoiseSigmas = {};
|
|
300
|
+
this.collectionSchemas = {};
|
|
295
301
|
this.host = host;
|
|
296
302
|
this.apiKey = apiKey;
|
|
297
303
|
this.userId = userId;
|
|
@@ -313,8 +319,223 @@ class HyperspaceClient {
|
|
|
313
319
|
this.metadata.add('x-hyperspace-user-id', userId);
|
|
314
320
|
}
|
|
315
321
|
}
|
|
322
|
+
registerCollectionKey(collectionName, key, metric = "l2", noiseSigma = 0.02, schema) {
|
|
323
|
+
this.collectionKeys[collectionName] = key;
|
|
324
|
+
this.collectionMetrics[collectionName] = metric;
|
|
325
|
+
this.collectionNoiseSigmas[collectionName] = noiseSigma;
|
|
326
|
+
if (schema) {
|
|
327
|
+
this.collectionSchemas[collectionName] = schema;
|
|
328
|
+
}
|
|
329
|
+
if (this.encryptionContexts[collectionName]) {
|
|
330
|
+
delete this.encryptionContexts[collectionName];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
deriveKeys(password, collectionName) {
|
|
334
|
+
const crypto = require('crypto');
|
|
335
|
+
const salt = crypto.createHash('sha256').update(collectionName).digest();
|
|
336
|
+
const aesKey = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
|
|
337
|
+
const hmacKey = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
|
|
338
|
+
return { aesKey, hmacKey };
|
|
339
|
+
}
|
|
340
|
+
encryptPayload(plaintext, aesKey) {
|
|
341
|
+
const crypto = require('crypto');
|
|
342
|
+
const iv = crypto.randomBytes(12);
|
|
343
|
+
const pbkdf2Salt = crypto.randomBytes(16);
|
|
344
|
+
const derivedAesKey = crypto.pbkdf2Sync(aesKey, pbkdf2Salt, 100000, 32, 'sha256');
|
|
345
|
+
const cipherPayload = crypto.createCipheriv('aes-256-gcm', derivedAesKey, iv);
|
|
346
|
+
const encryptedPayload = Buffer.concat([cipherPayload.update(plaintext), cipherPayload.final()]);
|
|
347
|
+
const payloadTag = cipherPayload.getAuthTag();
|
|
348
|
+
return Buffer.concat([pbkdf2Salt, iv, encryptedPayload, payloadTag]);
|
|
349
|
+
}
|
|
350
|
+
decryptPayload(data, aesKey) {
|
|
351
|
+
const crypto = require('crypto');
|
|
352
|
+
if (data.length < 16 + 12 + 16) {
|
|
353
|
+
throw new Error("Invalid encrypted payload size");
|
|
354
|
+
}
|
|
355
|
+
const pbkdf2Salt = data.subarray(0, 16);
|
|
356
|
+
const iv = data.subarray(16, 28);
|
|
357
|
+
const ciphertext = data.subarray(28, data.length - 16);
|
|
358
|
+
const tag = data.subarray(data.length - 16);
|
|
359
|
+
const derivedAesKey = crypto.pbkdf2Sync(aesKey, pbkdf2Salt, 100000, 32, 'sha256');
|
|
360
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', derivedAesKey, iv);
|
|
361
|
+
decipher.setAuthTag(tag);
|
|
362
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
363
|
+
}
|
|
364
|
+
hashMetadataKey(key, hmacKey) {
|
|
365
|
+
const crypto = require('crypto');
|
|
366
|
+
const hash = crypto.createHmac('sha256', hmacKey).update(key).digest('hex');
|
|
367
|
+
return "tag_" + hash.slice(0, 16);
|
|
368
|
+
}
|
|
369
|
+
hashMetadataValue(value, hmacKey) {
|
|
370
|
+
const crypto = require('crypto');
|
|
371
|
+
const hash = crypto.createHmac('sha256', hmacKey).update(value).digest('hex');
|
|
372
|
+
return "val_" + hash;
|
|
373
|
+
}
|
|
374
|
+
async _getEncryptionContext(collection, vectorDim, metric = "l2") {
|
|
375
|
+
if (!collection)
|
|
376
|
+
return null;
|
|
377
|
+
const key = this.collectionKeys[collection];
|
|
378
|
+
if (!key)
|
|
379
|
+
return null;
|
|
380
|
+
if (!this.collectionSchemas[collection]) {
|
|
381
|
+
try {
|
|
382
|
+
const stats = await this.getCollectionStats(collection);
|
|
383
|
+
if (stats && stats.schema) {
|
|
384
|
+
this.collectionSchemas[collection] = stats.schema;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
catch (e) { }
|
|
388
|
+
}
|
|
389
|
+
if (!this.encryptionContexts[collection]) {
|
|
390
|
+
const { aesKey, hmacKey } = this.deriveKeys(key, collection);
|
|
391
|
+
this.encryptionContexts[collection] = {
|
|
392
|
+
aesKey,
|
|
393
|
+
hmacKey,
|
|
394
|
+
projectionMatrices: {}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const context = this.encryptionContexts[collection];
|
|
398
|
+
if (vectorDim !== undefined) {
|
|
399
|
+
if (!context.projectionMatrices[vectorDim]) {
|
|
400
|
+
const isLorentz = ["lorentz", "poincare"].includes(metric.toLowerCase());
|
|
401
|
+
const matrixDim = metric.toLowerCase() === "poincare" ? vectorDim + 1 : vectorDim;
|
|
402
|
+
if (isLorentz) {
|
|
403
|
+
context.projectionMatrices[vectorDim] = CognitiveMath.generateLorentzMatrix(matrixDim, context.hmacKey);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
context.projectionMatrices[vectorDim] = CognitiveMath.generateOrthogonalMatrix(matrixDim, context.hmacKey);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return context;
|
|
411
|
+
}
|
|
412
|
+
_projectSingleBlock(subVec, metric, context, blockId) {
|
|
413
|
+
const dim = subVec.length;
|
|
414
|
+
if (dim === 0)
|
|
415
|
+
return [];
|
|
416
|
+
const cacheKey = blockId ? `${dim}_${blockId}` : `${dim}`;
|
|
417
|
+
if (!context.projectionMatrices[cacheKey]) {
|
|
418
|
+
const isLorentz = ["lorentz", "poincare"].includes(metric.toLowerCase());
|
|
419
|
+
const matrixDim = metric.toLowerCase() === "poincare" ? dim + 1 : dim;
|
|
420
|
+
const crypto = require('crypto');
|
|
421
|
+
let seed = context.hmacKey;
|
|
422
|
+
if (blockId) {
|
|
423
|
+
seed = crypto.createHash('sha256').update(seed).update(blockId).digest();
|
|
424
|
+
}
|
|
425
|
+
if (isLorentz) {
|
|
426
|
+
context.projectionMatrices[cacheKey] = CognitiveMath.generateLorentzMatrix(matrixDim, seed);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
context.projectionMatrices[cacheKey] = CognitiveMath.generateOrthogonalMatrix(matrixDim, seed);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
const matrix = context.projectionMatrices[cacheKey];
|
|
433
|
+
if (metric.toLowerCase() === "poincare") {
|
|
434
|
+
const lorentzVec = CognitiveMath.poincareToLorentz(subVec);
|
|
435
|
+
const projLorentz = CognitiveMath.projectVector(lorentzVec, matrix);
|
|
436
|
+
return CognitiveMath.lorentzToPoincare(projLorentz);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
return CognitiveMath.projectVector(subVec, matrix);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
_projectCollectionVector(collection, vector, context, metric = "l2") {
|
|
443
|
+
const schema = this.collectionSchemas[collection];
|
|
444
|
+
if (!schema || !schema.components || schema.components.length === 0) {
|
|
445
|
+
return this._projectSingleBlock(vector, metric, context);
|
|
446
|
+
}
|
|
447
|
+
const components = schema.components;
|
|
448
|
+
const cascade = schema.cascadePipeline || [];
|
|
449
|
+
const componentCutoffs = {};
|
|
450
|
+
for (const layer of cascade) {
|
|
451
|
+
const compName = layer.componentName;
|
|
452
|
+
const cutoff = layer.cutoffDimension;
|
|
453
|
+
if (compName && cutoff) {
|
|
454
|
+
if (!componentCutoffs[compName]) {
|
|
455
|
+
componentCutoffs[compName] = [];
|
|
456
|
+
}
|
|
457
|
+
componentCutoffs[compName].push(cutoff);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
for (const compName in componentCutoffs) {
|
|
461
|
+
componentCutoffs[compName] = Array.from(new Set(componentCutoffs[compName])).sort((a, b) => a - b);
|
|
462
|
+
}
|
|
463
|
+
const projectedParts = [];
|
|
464
|
+
let currentOffset = 0;
|
|
465
|
+
for (const comp of components) {
|
|
466
|
+
const compName = comp.name;
|
|
467
|
+
const compMetric = comp.metric;
|
|
468
|
+
const compDim = comp.fullDimension;
|
|
469
|
+
if (currentOffset >= vector.length) {
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
let subVec = vector.slice(currentOffset, currentOffset + compDim);
|
|
473
|
+
if (subVec.length < compDim) {
|
|
474
|
+
subVec = subVec.concat(new Array(compDim - subVec.length).fill(0));
|
|
475
|
+
}
|
|
476
|
+
const cutoffs = componentCutoffs[compName] || [];
|
|
477
|
+
const validCutoffs = cutoffs.filter(c => c < compDim);
|
|
478
|
+
let projSub = [];
|
|
479
|
+
if (validCutoffs.length === 0) {
|
|
480
|
+
projSub = this._projectSingleBlock(subVec, compMetric, context);
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
let blockStart = 0;
|
|
484
|
+
for (const cutoff of validCutoffs) {
|
|
485
|
+
const blockData = subVec.slice(blockStart, cutoff);
|
|
486
|
+
const projBlock = this._projectSingleBlock(blockData, compMetric, context, `${compName}_block_${blockStart}_${cutoff}`);
|
|
487
|
+
projSub = projSub.concat(projBlock);
|
|
488
|
+
blockStart = cutoff;
|
|
489
|
+
}
|
|
490
|
+
if (blockStart < compDim) {
|
|
491
|
+
const blockData = subVec.slice(blockStart, compDim);
|
|
492
|
+
const projBlock = this._projectSingleBlock(blockData, compMetric, context, `${compName}_block_${blockStart}_${compDim}`);
|
|
493
|
+
projSub = projSub.concat(projBlock);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
projectedParts.push(...projSub);
|
|
497
|
+
currentOffset += compDim;
|
|
498
|
+
}
|
|
499
|
+
if (currentOffset < vector.length) {
|
|
500
|
+
projectedParts.push(...vector.slice(currentOffset));
|
|
501
|
+
}
|
|
502
|
+
return projectedParts;
|
|
503
|
+
}
|
|
504
|
+
_encryptFilters(filters, context) {
|
|
505
|
+
if (!filters)
|
|
506
|
+
return filters;
|
|
507
|
+
return filters.map(f => {
|
|
508
|
+
const nf = { ...f };
|
|
509
|
+
if (nf.match) {
|
|
510
|
+
nf.match = {
|
|
511
|
+
key: this.hashMetadataKey(nf.match.key, context.hmacKey),
|
|
512
|
+
value: this.hashMetadataValue(nf.match.value, context.hmacKey)
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
if (nf.prefix) {
|
|
516
|
+
nf.prefix = {
|
|
517
|
+
key: this.hashMetadataKey(nf.prefix.key, context.hmacKey),
|
|
518
|
+
prefix: this.hashMetadataValue(nf.prefix.prefix, context.hmacKey)
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
if (nf.and) {
|
|
522
|
+
nf.and = this._encryptFilters(nf.and, context);
|
|
523
|
+
}
|
|
524
|
+
if (nf.or) {
|
|
525
|
+
nf.or = this._encryptFilters(nf.or, context);
|
|
526
|
+
}
|
|
527
|
+
if (nf.not) {
|
|
528
|
+
nf.not = this._encryptFilters([nf.not], context)[0];
|
|
529
|
+
}
|
|
530
|
+
return nf;
|
|
531
|
+
});
|
|
532
|
+
}
|
|
316
533
|
// ... (create/delete unchanged) ...
|
|
317
|
-
createCollection(name, schema) {
|
|
534
|
+
createCollection(name, schema, encryptionKey = '', noiseSigma = 0.02) {
|
|
535
|
+
const metric = (schema.components && schema.components[0]) ? schema.components[0].metric : "l2";
|
|
536
|
+
if (encryptionKey) {
|
|
537
|
+
this.registerCollectionKey(name, encryptionKey, metric, noiseSigma, schema);
|
|
538
|
+
}
|
|
318
539
|
return new Promise((resolve, reject) => {
|
|
319
540
|
const req = new hyperspace_pb_1.CreateCollectionRequest();
|
|
320
541
|
req.setName(name);
|
|
@@ -443,35 +664,85 @@ class HyperspaceClient {
|
|
|
443
664
|
}
|
|
444
665
|
insert(id, vector, meta, collection = '', durability = hyperspace_pb_1.DurabilityLevel.DEFAULT_LEVEL, typedMetadata, payload // Sidecar Payload Storage (v3.2)
|
|
445
666
|
) {
|
|
446
|
-
return new Promise((resolve, reject) => {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
667
|
+
return new Promise(async (resolve, reject) => {
|
|
668
|
+
var _a;
|
|
669
|
+
try {
|
|
670
|
+
const req = new hyperspace_pb_1.InsertRequest();
|
|
671
|
+
let vectorList = HyperspaceClient.toVectorList(vector);
|
|
672
|
+
const metric = this.collectionMetrics[collection] || "l2";
|
|
673
|
+
const context = await this._getEncryptionContext(collection, vectorList.length, metric);
|
|
674
|
+
if (context) {
|
|
675
|
+
const sigma = (_a = this.collectionNoiseSigmas[collection]) !== null && _a !== void 0 ? _a : 0.02;
|
|
676
|
+
if (sigma > 0.0) {
|
|
677
|
+
vectorList = CognitiveMath.injectAnisotropicNoise(vectorList, context.hmacKey, sigma);
|
|
678
|
+
}
|
|
679
|
+
vectorList = this._projectCollectionVector(collection, vectorList, context, metric);
|
|
680
|
+
let rawPayload = null;
|
|
681
|
+
if (payload) {
|
|
682
|
+
rawPayload = Buffer.from(payload);
|
|
683
|
+
}
|
|
684
|
+
if (rawPayload) {
|
|
685
|
+
const encrypted = this.encryptPayload(rawPayload, context.aesKey);
|
|
686
|
+
req.setPayload(new Uint8Array(encrypted));
|
|
687
|
+
}
|
|
688
|
+
if (meta) {
|
|
689
|
+
const map = req.getMetadataMap();
|
|
690
|
+
for (const k in meta) {
|
|
691
|
+
const ek = this.hashMetadataKey(k, context.hmacKey);
|
|
692
|
+
const ev = this.hashMetadataValue(meta[k], context.hmacKey);
|
|
693
|
+
map.set(ek, ev);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (typedMetadata) {
|
|
697
|
+
const map = req.getTypedMetadataMap();
|
|
698
|
+
for (const k in typedMetadata) {
|
|
699
|
+
const ek = this.hashMetadataKey(k, context.hmacKey);
|
|
700
|
+
const ev = this.hashMetadataValue(String(typedMetadata[k]), context.hmacKey);
|
|
701
|
+
map.set(ek, HyperspaceClient.toProtoMetadataValue(ev));
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
if (meta) {
|
|
707
|
+
const map = req.getMetadataMap();
|
|
708
|
+
for (const k in meta)
|
|
709
|
+
map.set(k, meta[k]);
|
|
710
|
+
}
|
|
711
|
+
if (typedMetadata) {
|
|
712
|
+
const map = req.getTypedMetadataMap();
|
|
713
|
+
for (const k in typedMetadata)
|
|
714
|
+
map.set(k, HyperspaceClient.toProtoMetadataValue(typedMetadata[k]));
|
|
715
|
+
}
|
|
716
|
+
if (payload) {
|
|
717
|
+
req.setPayload(payload);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
req.setVectorList(vectorList);
|
|
721
|
+
req.setId(id);
|
|
722
|
+
req.setCollection(collection);
|
|
723
|
+
req.setOriginNodeId('');
|
|
724
|
+
req.setLogicalClock(0);
|
|
725
|
+
req.setDurability(durability);
|
|
726
|
+
this.client.insert(req, this.metadata, (err, resp) => {
|
|
727
|
+
if (err)
|
|
728
|
+
return reject(err);
|
|
729
|
+
resolve(resp.getSuccess());
|
|
730
|
+
});
|
|
459
731
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
req.setLogicalClock(0);
|
|
463
|
-
req.setDurability(durability);
|
|
464
|
-
if (payload) {
|
|
465
|
-
req.setPayload(payload);
|
|
732
|
+
catch (e) {
|
|
733
|
+
reject(e);
|
|
466
734
|
}
|
|
467
|
-
this.client.insert(req, this.metadata, (err, resp) => {
|
|
468
|
-
if (err)
|
|
469
|
-
return reject(err);
|
|
470
|
-
resolve(resp.getSuccess());
|
|
471
|
-
});
|
|
472
735
|
});
|
|
473
736
|
}
|
|
474
737
|
insertText(id, text, meta, collection = '', durability = hyperspace_pb_1.DurabilityLevel.DEFAULT_LEVEL) {
|
|
738
|
+
if (this.collectionKeys[collection]) {
|
|
739
|
+
if (!this.embedder) {
|
|
740
|
+
return Promise.reject(new Error("An embedder must be configured to use insertText on encrypted collections."));
|
|
741
|
+
}
|
|
742
|
+
return Promise.resolve(this.embedder.encode(text)).then(vector => {
|
|
743
|
+
return this.insert(id, vector, meta, collection, durability, undefined, Buffer.from(text, 'utf-8'));
|
|
744
|
+
});
|
|
745
|
+
}
|
|
475
746
|
return new Promise((resolve, reject) => {
|
|
476
747
|
const req = new hyperspace_pb_1.InsertTextRequest();
|
|
477
748
|
req.setText(text);
|
|
@@ -532,85 +803,135 @@ class HyperspaceClient {
|
|
|
532
803
|
});
|
|
533
804
|
}
|
|
534
805
|
search(vector, topK, collection = '', options) {
|
|
535
|
-
return new Promise((resolve, reject) => {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
806
|
+
return new Promise(async (resolve, reject) => {
|
|
807
|
+
var _a;
|
|
808
|
+
try {
|
|
809
|
+
const req = new hyperspace_pb_1.SearchRequest();
|
|
810
|
+
let vectorList = HyperspaceClient.toVectorList(vector);
|
|
811
|
+
req.setTopK(topK);
|
|
812
|
+
req.setCollection(collection);
|
|
813
|
+
const metric = this.collectionMetrics[collection] || "l2";
|
|
814
|
+
const context = await this._getEncryptionContext(collection, vectorList.length, metric);
|
|
815
|
+
let optFilters = options === null || options === void 0 ? void 0 : options.filters;
|
|
816
|
+
let optFilter = options === null || options === void 0 ? void 0 : options.filter;
|
|
817
|
+
let includePayload = options === null || options === void 0 ? void 0 : options.includePayload;
|
|
818
|
+
if (context) {
|
|
819
|
+
const sigma = (_a = this.collectionNoiseSigmas[collection]) !== null && _a !== void 0 ? _a : 0.02;
|
|
820
|
+
if (sigma > 0.0) {
|
|
821
|
+
vectorList = CognitiveMath.injectAnisotropicNoise(vectorList, context.hmacKey, sigma);
|
|
822
|
+
}
|
|
823
|
+
vectorList = this._projectCollectionVector(collection, vectorList, context, metric);
|
|
824
|
+
if (optFilter) {
|
|
825
|
+
const hashedFilter = {};
|
|
826
|
+
for (const k in optFilter) {
|
|
827
|
+
const ek = this.hashMetadataKey(k, context.hmacKey);
|
|
828
|
+
const ev = this.hashMetadataValue(optFilter[k], context.hmacKey);
|
|
829
|
+
hashedFilter[ek] = ev;
|
|
830
|
+
}
|
|
831
|
+
optFilter = hashedFilter;
|
|
832
|
+
}
|
|
833
|
+
if (optFilters) {
|
|
834
|
+
optFilters = this._encryptFilters(optFilters, context);
|
|
835
|
+
}
|
|
836
|
+
includePayload = true;
|
|
544
837
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
req.setUseWave(options.useWave);
|
|
552
|
-
}
|
|
553
|
-
if (options === null || options === void 0 ? void 0 : options.filters) {
|
|
554
|
-
req.setFiltersList(options.filters.map(f => this.toProtoFilter(f)));
|
|
555
|
-
}
|
|
556
|
-
if (options === null || options === void 0 ? void 0 : options.hybridQuery)
|
|
557
|
-
req.setHybridQuery(options.hybridQuery);
|
|
558
|
-
if ((options === null || options === void 0 ? void 0 : options.hybridAlpha) !== undefined)
|
|
559
|
-
req.setHybridAlpha(options.hybridAlpha);
|
|
560
|
-
if ((options === null || options === void 0 ? void 0 : options.mrlDimension) !== undefined)
|
|
561
|
-
req.setMrlDimension(options.mrlDimension);
|
|
562
|
-
if ((options === null || options === void 0 ? void 0 : options.useWasserstein) !== undefined)
|
|
563
|
-
req.setUseWasserstein(options.useWasserstein);
|
|
564
|
-
if ((options === null || options === void 0 ? void 0 : options.includePayload) !== undefined)
|
|
565
|
-
req.setIncludePayload(options.includePayload);
|
|
566
|
-
if (options === null || options === void 0 ? void 0 : options.componentWeights) {
|
|
567
|
-
const map = req.getComponentWeightsMap();
|
|
568
|
-
for (const k in options.componentWeights) {
|
|
569
|
-
map.set(k, options.componentWeights[k]);
|
|
838
|
+
req.setVectorList(vectorList);
|
|
839
|
+
if (optFilter) {
|
|
840
|
+
const map = req.getFilterMap();
|
|
841
|
+
for (const k in optFilter) {
|
|
842
|
+
map.set(k, optFilter[k]);
|
|
843
|
+
}
|
|
570
844
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if (
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
const meta = {};
|
|
596
|
-
if (metaMap.getLength() > 0) {
|
|
597
|
-
metaMap.forEach((entry, key) => {
|
|
598
|
-
meta[key] = entry;
|
|
599
|
-
});
|
|
845
|
+
if ((options === null || options === void 0 ? void 0 : options.restartFactor) !== undefined) {
|
|
846
|
+
const map = req.getFilterMap();
|
|
847
|
+
map.set('wave_restart_factor', options.restartFactor.toString());
|
|
848
|
+
}
|
|
849
|
+
if ((options === null || options === void 0 ? void 0 : options.useWave) !== undefined) {
|
|
850
|
+
req.setUseWave(options.useWave);
|
|
851
|
+
}
|
|
852
|
+
if (optFilters) {
|
|
853
|
+
req.setFiltersList(optFilters.map(f => this.toProtoFilter(f)));
|
|
854
|
+
}
|
|
855
|
+
if (options === null || options === void 0 ? void 0 : options.hybridQuery)
|
|
856
|
+
req.setHybridQuery(options.hybridQuery);
|
|
857
|
+
if ((options === null || options === void 0 ? void 0 : options.hybridAlpha) !== undefined)
|
|
858
|
+
req.setHybridAlpha(options.hybridAlpha);
|
|
859
|
+
if ((options === null || options === void 0 ? void 0 : options.mrlDimension) !== undefined)
|
|
860
|
+
req.setMrlDimension(options.mrlDimension);
|
|
861
|
+
if ((options === null || options === void 0 ? void 0 : options.useWasserstein) !== undefined)
|
|
862
|
+
req.setUseWasserstein(options.useWasserstein);
|
|
863
|
+
if (includePayload !== undefined)
|
|
864
|
+
req.setIncludePayload(includePayload);
|
|
865
|
+
if (options === null || options === void 0 ? void 0 : options.componentWeights) {
|
|
866
|
+
const map = req.getComponentWeightsMap();
|
|
867
|
+
for (const k in options.componentWeights) {
|
|
868
|
+
map.set(k, options.componentWeights[k]);
|
|
600
869
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
870
|
+
}
|
|
871
|
+
if (options === null || options === void 0 ? void 0 : options.bm25) {
|
|
872
|
+
const bm25Msg = new hyperspace_pb_1.Bm25Options();
|
|
873
|
+
if (options.bm25.method !== undefined)
|
|
874
|
+
bm25Msg.setMethod(options.bm25.method);
|
|
875
|
+
if (options.bm25.k1 !== undefined)
|
|
876
|
+
bm25Msg.setK1(options.bm25.k1);
|
|
877
|
+
if (options.bm25.b !== undefined)
|
|
878
|
+
bm25Msg.setB(options.bm25.b);
|
|
879
|
+
if (options.bm25.delta !== undefined)
|
|
880
|
+
bm25Msg.setDelta(options.bm25.delta);
|
|
881
|
+
if (options.bm25.language !== undefined)
|
|
882
|
+
bm25Msg.setLanguage(options.bm25.language);
|
|
883
|
+
if (options.bm25.ngrams !== undefined)
|
|
884
|
+
bm25Msg.setNgrams(options.bm25.ngrams);
|
|
885
|
+
if (options.bm25.fusionMethod !== undefined)
|
|
886
|
+
bm25Msg.setFusionMethod(options.bm25.fusionMethod);
|
|
887
|
+
req.setBm25Options(bm25Msg);
|
|
888
|
+
}
|
|
889
|
+
this.client.search(req, this.metadata, (err, resp) => {
|
|
890
|
+
if (err)
|
|
891
|
+
return reject(err);
|
|
892
|
+
const results = resp.getResultsList().map(r => {
|
|
893
|
+
const metaMap = r.getMetadataMap();
|
|
894
|
+
const meta = {};
|
|
895
|
+
if (metaMap.getLength() > 0) {
|
|
896
|
+
metaMap.forEach((entry, key) => {
|
|
897
|
+
meta[key] = entry;
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
let payloadBytes = r.getPayload_asU8();
|
|
901
|
+
if (context && payloadBytes && payloadBytes.length > 0) {
|
|
902
|
+
try {
|
|
903
|
+
const decrypted = this.decryptPayload(Buffer.from(payloadBytes), context.aesKey);
|
|
904
|
+
payloadBytes = new Uint8Array(decrypted);
|
|
905
|
+
}
|
|
906
|
+
catch (e) {
|
|
907
|
+
// Decryption error
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return {
|
|
911
|
+
id: r.getId(),
|
|
912
|
+
distance: r.getDistance(),
|
|
913
|
+
metadata: meta,
|
|
914
|
+
typedMetadata: HyperspaceClient.parseTypedMetadata(r.getTypedMetadataMap()),
|
|
915
|
+
payload: payloadBytes
|
|
916
|
+
};
|
|
917
|
+
});
|
|
918
|
+
resolve(results);
|
|
608
919
|
});
|
|
609
|
-
|
|
610
|
-
|
|
920
|
+
}
|
|
921
|
+
catch (e) {
|
|
922
|
+
reject(e);
|
|
923
|
+
}
|
|
611
924
|
});
|
|
612
925
|
}
|
|
613
926
|
searchText(text, topK, collection = '', options) {
|
|
927
|
+
if (this.collectionKeys[collection]) {
|
|
928
|
+
if (!this.embedder) {
|
|
929
|
+
return Promise.reject(new Error("An embedder must be configured to use searchText on encrypted collections."));
|
|
930
|
+
}
|
|
931
|
+
return Promise.resolve(this.embedder.encode(text)).then(vector => {
|
|
932
|
+
return this.search(vector, topK, collection, options);
|
|
933
|
+
});
|
|
934
|
+
}
|
|
614
935
|
return new Promise((resolve, reject) => {
|
|
615
936
|
const req = new hyperspace_pb_1.SearchTextRequest();
|
|
616
937
|
req.setText(text);
|
package/dist/math.d.ts
CHANGED
|
@@ -42,3 +42,7 @@ export declare function koopmanExtrapolate(past: number[], current: number[], st
|
|
|
42
42
|
* Pulls the thought towards the context along the geodesic by `resonanceFactor` [0, 1].
|
|
43
43
|
*/
|
|
44
44
|
export declare function contextResonance(thought: number[], globalContext: number[], resonanceFactor: number, c?: number): number[];
|
|
45
|
+
export declare function generateOrthogonalMatrix(dimension: number, seedBytes: Uint8Array): number[][];
|
|
46
|
+
export declare function generateLorentzMatrix(dimension: number, seedBytes: Uint8Array): number[][];
|
|
47
|
+
export declare function projectVector(v: number[], projectionMatrix: number[][]): number[];
|
|
48
|
+
export declare function injectAnisotropicNoise(vector: number[], noiseSeed: Uint8Array, sigma?: number): number[];
|
package/dist/math.js
CHANGED
|
@@ -21,6 +21,10 @@ exports.localEntropy = localEntropy;
|
|
|
21
21
|
exports.lyapunovConvergence = lyapunovConvergence;
|
|
22
22
|
exports.koopmanExtrapolate = koopmanExtrapolate;
|
|
23
23
|
exports.contextResonance = contextResonance;
|
|
24
|
+
exports.generateOrthogonalMatrix = generateOrthogonalMatrix;
|
|
25
|
+
exports.generateLorentzMatrix = generateLorentzMatrix;
|
|
26
|
+
exports.projectVector = projectVector;
|
|
27
|
+
exports.injectAnisotropicNoise = injectAnisotropicNoise;
|
|
24
28
|
function dot(a, b) {
|
|
25
29
|
let sum = 0;
|
|
26
30
|
for (let i = 0; i < a.length; i++)
|
|
@@ -232,3 +236,149 @@ function contextResonance(thought, globalContext, resonanceFactor, c = 1.0) {
|
|
|
232
236
|
const appliedPull = pullDir.map(v => v * factor);
|
|
233
237
|
return expMap(thought, appliedPull, c);
|
|
234
238
|
}
|
|
239
|
+
function generateOrthogonalMatrix(dimension, seedBytes) {
|
|
240
|
+
const crypto = require('crypto');
|
|
241
|
+
const matrix = [];
|
|
242
|
+
let currentHash = crypto.createHash('sha256').update(seedBytes).digest();
|
|
243
|
+
let hashOffset = 0;
|
|
244
|
+
for (let i = 0; i < dimension; i++) {
|
|
245
|
+
const row = [];
|
|
246
|
+
for (let j = 0; j < dimension; j++) {
|
|
247
|
+
if (hashOffset >= 32) {
|
|
248
|
+
currentHash = crypto.createHash('sha256').update(currentHash).digest();
|
|
249
|
+
hashOffset = 0;
|
|
250
|
+
}
|
|
251
|
+
const val = (currentHash.readUInt32LE(hashOffset) / 4294967296.0) * 2.0 - 1.0;
|
|
252
|
+
row.push(val);
|
|
253
|
+
hashOffset += 4;
|
|
254
|
+
}
|
|
255
|
+
matrix.push(row);
|
|
256
|
+
}
|
|
257
|
+
// Gram-Schmidt Orthonormalization with Reorthogonalization (twice is enough)
|
|
258
|
+
for (let i = 0; i < dimension; i++) {
|
|
259
|
+
let v = matrix[i];
|
|
260
|
+
for (let j = 0; j < i; j++) {
|
|
261
|
+
const u = matrix[j];
|
|
262
|
+
let uDotV = dot(u, v);
|
|
263
|
+
v = v.map((vi, k) => vi - uDotV * u[k]);
|
|
264
|
+
// Re-project to eliminate floating-point leakage
|
|
265
|
+
uDotV = dot(u, v);
|
|
266
|
+
v = v.map((vi, k) => vi - uDotV * u[k]);
|
|
267
|
+
}
|
|
268
|
+
const vNorm = norm(v);
|
|
269
|
+
if (vNorm > 1e-15) {
|
|
270
|
+
matrix[i] = v.map(vi => vi / vNorm);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
matrix[i] = new Array(dimension).fill(0);
|
|
274
|
+
matrix[i][i] = 1.0;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return matrix;
|
|
278
|
+
}
|
|
279
|
+
function generateLorentzMatrix(dimension, seedBytes) {
|
|
280
|
+
const d = dimension - 1;
|
|
281
|
+
const crypto = require('crypto');
|
|
282
|
+
const rSeed = crypto.createHash('sha256').update(seedBytes).update("spatial").digest();
|
|
283
|
+
const R = generateOrthogonalMatrix(d, rSeed);
|
|
284
|
+
const bSeed = crypto.createHash('sha256').update(seedBytes).update("boost").digest();
|
|
285
|
+
let beta = [];
|
|
286
|
+
let currentHash = bSeed;
|
|
287
|
+
let hashOffset = 0;
|
|
288
|
+
for (let i = 0; i < d; i++) {
|
|
289
|
+
if (hashOffset >= 32) {
|
|
290
|
+
currentHash = crypto.createHash('sha256').update(currentHash).digest();
|
|
291
|
+
hashOffset = 0;
|
|
292
|
+
}
|
|
293
|
+
const val = (currentHash.readUInt32LE(hashOffset) / 4294967296.0) * 2.0 - 1.0;
|
|
294
|
+
beta.push(val);
|
|
295
|
+
hashOffset += 4;
|
|
296
|
+
}
|
|
297
|
+
const betaNorm = norm(beta);
|
|
298
|
+
if (betaNorm > 1e-15) {
|
|
299
|
+
beta = beta.map(v => (v / betaNorm) * 0.1);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
beta = new Array(d).fill(0);
|
|
303
|
+
beta[0] = 0.1;
|
|
304
|
+
}
|
|
305
|
+
const betaSq = dot(beta, beta);
|
|
306
|
+
const gamma = 1.0 / Math.sqrt(1.0 - betaSq);
|
|
307
|
+
const Lambda_B = [];
|
|
308
|
+
for (let i = 0; i < dimension; i++) {
|
|
309
|
+
Lambda_B.push(new Array(dimension).fill(0));
|
|
310
|
+
}
|
|
311
|
+
Lambda_B[0][0] = gamma;
|
|
312
|
+
for (let j = 1; j < dimension; j++) {
|
|
313
|
+
Lambda_B[0][j] = -gamma * beta[j - 1];
|
|
314
|
+
Lambda_B[j][0] = -gamma * beta[j - 1];
|
|
315
|
+
}
|
|
316
|
+
const factor = (gamma - 1.0) / betaSq;
|
|
317
|
+
for (let i = 1; i < dimension; i++) {
|
|
318
|
+
for (let j = 1; j < dimension; j++) {
|
|
319
|
+
const delta = i === j ? 1.0 : 0.0;
|
|
320
|
+
Lambda_B[i][j] = delta + factor * beta[i - 1] * beta[j - 1];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const Lambda_R = [];
|
|
324
|
+
for (let i = 0; i < dimension; i++) {
|
|
325
|
+
Lambda_R.push(new Array(dimension).fill(0));
|
|
326
|
+
}
|
|
327
|
+
Lambda_R[0][0] = 1.0;
|
|
328
|
+
for (let i = 1; i < dimension; i++) {
|
|
329
|
+
for (let j = 1; j < dimension; j++) {
|
|
330
|
+
Lambda_R[i][j] = R[i - 1][j - 1];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const Lambda = [];
|
|
334
|
+
for (let i = 0; i < dimension; i++) {
|
|
335
|
+
const row = [];
|
|
336
|
+
for (let j = 0; j < dimension; j++) {
|
|
337
|
+
let sum = 0;
|
|
338
|
+
for (let k = 0; k < dimension; k++) {
|
|
339
|
+
sum += Lambda_R[i][k] * Lambda_B[k][j];
|
|
340
|
+
}
|
|
341
|
+
row.push(sum);
|
|
342
|
+
}
|
|
343
|
+
Lambda.push(row);
|
|
344
|
+
}
|
|
345
|
+
return Lambda;
|
|
346
|
+
}
|
|
347
|
+
function projectVector(v, projectionMatrix) {
|
|
348
|
+
const dimension = v.length;
|
|
349
|
+
const projected = new Array(dimension).fill(0);
|
|
350
|
+
for (let j = 0; j < dimension; j++) {
|
|
351
|
+
let sum = 0;
|
|
352
|
+
for (let i = 0; i < dimension; i++) {
|
|
353
|
+
sum += v[i] * projectionMatrix[i][j];
|
|
354
|
+
}
|
|
355
|
+
projected[j] = sum;
|
|
356
|
+
}
|
|
357
|
+
return projected;
|
|
358
|
+
}
|
|
359
|
+
function injectAnisotropicNoise(vector, noiseSeed, sigma = 0.02) {
|
|
360
|
+
if (sigma <= 0.0)
|
|
361
|
+
return [...vector];
|
|
362
|
+
const crypto = require('crypto');
|
|
363
|
+
const vecBytes = Buffer.from(vector.toString());
|
|
364
|
+
const hash = crypto.createHash('sha256').update(noiseSeed).update(vecBytes).digest();
|
|
365
|
+
let currentHash = hash;
|
|
366
|
+
const noise = [];
|
|
367
|
+
for (let i = 0; i < vector.length; i++) {
|
|
368
|
+
if (currentHash.length < 16) {
|
|
369
|
+
currentHash = crypto.createHash('sha256').update(currentHash).digest();
|
|
370
|
+
}
|
|
371
|
+
const u1 = Math.max(1e-15, (currentHash.readUInt32LE(0) + 1) / 4294967296);
|
|
372
|
+
const u2 = Math.max(1e-15, (currentHash.readUInt32LE(4) + 1) / 4294967296);
|
|
373
|
+
const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
|
|
374
|
+
noise.push(z0 * sigma);
|
|
375
|
+
currentHash = currentHash.subarray(8);
|
|
376
|
+
}
|
|
377
|
+
const noisy = vector.map((v, i) => v + noise[i]);
|
|
378
|
+
const origNorm = norm(vector);
|
|
379
|
+
const noisyNorm = norm(noisy);
|
|
380
|
+
if (noisyNorm > 1e-15) {
|
|
381
|
+
return noisy.map(v => (v / noisyNorm) * origNorm);
|
|
382
|
+
}
|
|
383
|
+
return noisy;
|
|
384
|
+
}
|