@sparkleideas/ruvector-upstream 3.0.0-alpha.2
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 +71 -0
- package/package.json +125 -0
- package/src/bridges/attention.ts +185 -0
- package/src/bridges/cognitive.ts +343 -0
- package/src/bridges/exotic.ts +277 -0
- package/src/bridges/gnn.ts +177 -0
- package/src/bridges/hnsw.ts +199 -0
- package/src/bridges/hyperbolic.ts +268 -0
- package/src/bridges/learning.ts +238 -0
- package/src/bridges/sona.ts +418 -0
- package/src/index.ts +22 -0
- package/src/registry.ts +182 -0
- package/src/types.ts +146 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph Neural Network Bridge
|
|
3
|
+
*
|
|
4
|
+
* Bridge to ruvector-gnn-wasm for graph-based learning and inference.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { WasmBridge, WasmModuleStatus, GnnConfig } from '../types.js';
|
|
8
|
+
import { GnnConfigSchema } from '../types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Graph structure
|
|
12
|
+
*/
|
|
13
|
+
export interface Graph {
|
|
14
|
+
nodes: Float32Array[];
|
|
15
|
+
edges: Array<[number, number]>;
|
|
16
|
+
edgeWeights?: Float32Array;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* GNN inference result
|
|
21
|
+
*/
|
|
22
|
+
export interface GnnResult {
|
|
23
|
+
nodeEmbeddings: Float32Array[];
|
|
24
|
+
graphEmbedding: Float32Array;
|
|
25
|
+
predictions?: Float32Array;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* GNN WASM module interface
|
|
30
|
+
*/
|
|
31
|
+
interface GnnModule {
|
|
32
|
+
forward(graph: Graph, config: GnnConfig): GnnResult;
|
|
33
|
+
nodeClassification(graph: Graph, config: GnnConfig): Float32Array;
|
|
34
|
+
linkPrediction(graph: Graph, source: number, targets: number[], config: GnnConfig): Float32Array;
|
|
35
|
+
graphClassification(graphs: Graph[], config: GnnConfig): Float32Array;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* GNN Bridge implementation
|
|
40
|
+
*/
|
|
41
|
+
export class GnnBridge implements WasmBridge<GnnModule> {
|
|
42
|
+
readonly name = 'ruvector-gnn-wasm';
|
|
43
|
+
readonly version = '0.1.0';
|
|
44
|
+
|
|
45
|
+
private _status: WasmModuleStatus = 'unloaded';
|
|
46
|
+
private _module: GnnModule | null = null;
|
|
47
|
+
private config: GnnConfig;
|
|
48
|
+
|
|
49
|
+
constructor(config?: Partial<GnnConfig>) {
|
|
50
|
+
this.config = GnnConfigSchema.parse(config ?? {});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get status(): WasmModuleStatus {
|
|
54
|
+
return this._status;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async init(): Promise<void> {
|
|
58
|
+
if (this._status === 'ready') return;
|
|
59
|
+
if (this._status === 'loading') return;
|
|
60
|
+
|
|
61
|
+
this._status = 'loading';
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const wasmModule = await import('@ruvector/gnn-wasm').catch(() => null);
|
|
65
|
+
|
|
66
|
+
if (wasmModule) {
|
|
67
|
+
this._module = wasmModule as unknown as GnnModule;
|
|
68
|
+
} else {
|
|
69
|
+
this._module = this.createMockModule();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this._status = 'ready';
|
|
73
|
+
} catch (error) {
|
|
74
|
+
this._status = 'error';
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async destroy(): Promise<void> {
|
|
80
|
+
this._module = null;
|
|
81
|
+
this._status = 'unloaded';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
isReady(): boolean {
|
|
85
|
+
return this._status === 'ready';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getModule(): GnnModule | null {
|
|
89
|
+
return this._module;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Forward pass through GNN
|
|
94
|
+
*/
|
|
95
|
+
forward(graph: Graph, config?: Partial<GnnConfig>): GnnResult {
|
|
96
|
+
if (!this._module) throw new Error('GNN module not initialized');
|
|
97
|
+
const mergedConfig = { ...this.config, ...config };
|
|
98
|
+
return this._module.forward(graph, mergedConfig);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Node classification
|
|
103
|
+
*/
|
|
104
|
+
nodeClassification(graph: Graph, config?: Partial<GnnConfig>): Float32Array {
|
|
105
|
+
if (!this._module) throw new Error('GNN module not initialized');
|
|
106
|
+
const mergedConfig = { ...this.config, ...config };
|
|
107
|
+
return this._module.nodeClassification(graph, mergedConfig);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Link prediction
|
|
112
|
+
*/
|
|
113
|
+
linkPrediction(
|
|
114
|
+
graph: Graph,
|
|
115
|
+
source: number,
|
|
116
|
+
targets: number[],
|
|
117
|
+
config?: Partial<GnnConfig>
|
|
118
|
+
): Float32Array {
|
|
119
|
+
if (!this._module) throw new Error('GNN module not initialized');
|
|
120
|
+
const mergedConfig = { ...this.config, ...config };
|
|
121
|
+
return this._module.linkPrediction(graph, source, targets, mergedConfig);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Graph classification
|
|
126
|
+
*/
|
|
127
|
+
graphClassification(graphs: Graph[], config?: Partial<GnnConfig>): Float32Array {
|
|
128
|
+
if (!this._module) throw new Error('GNN module not initialized');
|
|
129
|
+
const mergedConfig = { ...this.config, ...config };
|
|
130
|
+
return this._module.graphClassification(graphs, mergedConfig);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Create mock module for development
|
|
135
|
+
*/
|
|
136
|
+
private createMockModule(): GnnModule {
|
|
137
|
+
return {
|
|
138
|
+
forward(graph: Graph, config: GnnConfig): GnnResult {
|
|
139
|
+
const nodeEmbeddings = graph.nodes.map(node => {
|
|
140
|
+
const emb = new Float32Array(config.outputDim);
|
|
141
|
+
for (let i = 0; i < config.outputDim; i++) {
|
|
142
|
+
emb[i] = node[i % node.length] * 0.5;
|
|
143
|
+
}
|
|
144
|
+
return emb;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const graphEmbedding = new Float32Array(config.outputDim);
|
|
148
|
+
for (const nodeEmb of nodeEmbeddings) {
|
|
149
|
+
for (let i = 0; i < config.outputDim; i++) {
|
|
150
|
+
graphEmbedding[i] += nodeEmb[i] / nodeEmbeddings.length;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { nodeEmbeddings, graphEmbedding };
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
nodeClassification(graph: Graph, config: GnnConfig): Float32Array {
|
|
158
|
+
return new Float32Array(graph.nodes.length).fill(0.5);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
linkPrediction(graph: Graph, source: number, targets: number[], config: GnnConfig): Float32Array {
|
|
162
|
+
return new Float32Array(targets.length).map(() => Math.random());
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
graphClassification(graphs: Graph[], config: GnnConfig): Float32Array {
|
|
166
|
+
return new Float32Array(graphs.length).map(() => Math.random());
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Create a new GNN bridge
|
|
174
|
+
*/
|
|
175
|
+
export function createGnnBridge(config?: Partial<GnnConfig>): GnnBridge {
|
|
176
|
+
return new GnnBridge(config);
|
|
177
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HNSW Bridge
|
|
3
|
+
*
|
|
4
|
+
* Bridge to micro-hnsw-wasm for ultra-fast vector similarity search.
|
|
5
|
+
* Achieves 150x-12,500x faster search compared to brute-force.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { WasmBridge, WasmModuleStatus, HnswConfig, SearchResult } from '../types.js';
|
|
9
|
+
import { HnswConfigSchema } from '../types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* HNSW WASM module interface
|
|
13
|
+
*/
|
|
14
|
+
interface HnswModule {
|
|
15
|
+
create(config: HnswConfig): HnswIndex;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* HNSW index interface
|
|
20
|
+
*/
|
|
21
|
+
interface HnswIndex {
|
|
22
|
+
add(id: string, vector: Float32Array, metadata?: Record<string, unknown>): void;
|
|
23
|
+
search(query: Float32Array, k: number): SearchResult[];
|
|
24
|
+
remove(id: string): boolean;
|
|
25
|
+
size(): number;
|
|
26
|
+
save(): Uint8Array;
|
|
27
|
+
load(data: Uint8Array): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* HNSW Bridge implementation
|
|
32
|
+
*/
|
|
33
|
+
export class HnswBridge implements WasmBridge<HnswModule> {
|
|
34
|
+
readonly name = 'micro-hnsw-wasm';
|
|
35
|
+
readonly version = '0.1.0';
|
|
36
|
+
|
|
37
|
+
private _status: WasmModuleStatus = 'unloaded';
|
|
38
|
+
private _module: HnswModule | null = null;
|
|
39
|
+
private _index: HnswIndex | null = null;
|
|
40
|
+
private config: HnswConfig;
|
|
41
|
+
|
|
42
|
+
constructor(config?: Partial<HnswConfig>) {
|
|
43
|
+
this.config = HnswConfigSchema.parse(config ?? {});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get status(): WasmModuleStatus {
|
|
47
|
+
return this._status;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async init(): Promise<void> {
|
|
51
|
+
if (this._status === 'ready') return;
|
|
52
|
+
if (this._status === 'loading') return;
|
|
53
|
+
|
|
54
|
+
this._status = 'loading';
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// Dynamic import of WASM module
|
|
58
|
+
const wasmModule = await import('@ruvector/micro-hnsw-wasm').catch(() => null);
|
|
59
|
+
|
|
60
|
+
if (wasmModule) {
|
|
61
|
+
this._module = wasmModule as unknown as HnswModule;
|
|
62
|
+
this._index = this._module.create(this.config);
|
|
63
|
+
} else {
|
|
64
|
+
// Fallback to mock implementation for development
|
|
65
|
+
this._module = this.createMockModule();
|
|
66
|
+
this._index = this._module.create(this.config);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this._status = 'ready';
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this._status = 'error';
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async destroy(): Promise<void> {
|
|
77
|
+
this._index = null;
|
|
78
|
+
this._module = null;
|
|
79
|
+
this._status = 'unloaded';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
isReady(): boolean {
|
|
83
|
+
return this._status === 'ready';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getModule(): HnswModule | null {
|
|
87
|
+
return this._module;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the HNSW index
|
|
92
|
+
*/
|
|
93
|
+
getIndex(): HnswIndex | null {
|
|
94
|
+
return this._index;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Add a vector to the index
|
|
99
|
+
*/
|
|
100
|
+
add(id: string, vector: Float32Array, metadata?: Record<string, unknown>): void {
|
|
101
|
+
if (!this._index) throw new Error('HNSW index not initialized');
|
|
102
|
+
this._index.add(id, vector, metadata);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Search for similar vectors
|
|
107
|
+
*/
|
|
108
|
+
search(query: Float32Array, k: number): SearchResult[] {
|
|
109
|
+
if (!this._index) throw new Error('HNSW index not initialized');
|
|
110
|
+
return this._index.search(query, k);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Remove a vector from the index
|
|
115
|
+
*/
|
|
116
|
+
remove(id: string): boolean {
|
|
117
|
+
if (!this._index) throw new Error('HNSW index not initialized');
|
|
118
|
+
return this._index.remove(id);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get index size
|
|
123
|
+
*/
|
|
124
|
+
size(): number {
|
|
125
|
+
return this._index?.size() ?? 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create mock module for development
|
|
130
|
+
*/
|
|
131
|
+
private createMockModule(): HnswModule {
|
|
132
|
+
return {
|
|
133
|
+
create: (config: HnswConfig) => {
|
|
134
|
+
const vectors = new Map<string, { vector: Float32Array; metadata?: Record<string, unknown> }>();
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
add(id: string, vector: Float32Array, metadata?: Record<string, unknown>) {
|
|
138
|
+
vectors.set(id, { vector: new Float32Array(vector), metadata });
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
search(query: Float32Array, k: number): SearchResult[] {
|
|
142
|
+
const results: SearchResult[] = [];
|
|
143
|
+
|
|
144
|
+
for (const [id, { vector, metadata }] of vectors) {
|
|
145
|
+
const score = cosineSimilarity(query, vector);
|
|
146
|
+
results.push({ id, score, vector, metadata });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
results.sort((a, b) => b.score - a.score);
|
|
150
|
+
return results.slice(0, k);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
remove(id: string): boolean {
|
|
154
|
+
return vectors.delete(id);
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
size(): number {
|
|
158
|
+
return vectors.size;
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
save(): Uint8Array {
|
|
162
|
+
return new Uint8Array(0);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
load(_data: Uint8Array): void {
|
|
166
|
+
// No-op for mock
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Cosine similarity helper
|
|
176
|
+
*/
|
|
177
|
+
function cosineSimilarity(a: Float32Array, b: Float32Array): number {
|
|
178
|
+
if (a.length !== b.length) return 0;
|
|
179
|
+
|
|
180
|
+
let dot = 0;
|
|
181
|
+
let normA = 0;
|
|
182
|
+
let normB = 0;
|
|
183
|
+
|
|
184
|
+
for (let i = 0; i < a.length; i++) {
|
|
185
|
+
dot += a[i] * b[i];
|
|
186
|
+
normA += a[i] * a[i];
|
|
187
|
+
normB += b[i] * b[i];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
191
|
+
return denom > 0 ? dot / denom : 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Create a new HNSW bridge
|
|
196
|
+
*/
|
|
197
|
+
export function createHnswBridge(config?: Partial<HnswConfig>): HnswBridge {
|
|
198
|
+
return new HnswBridge(config);
|
|
199
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hyperbolic Embeddings Bridge
|
|
3
|
+
*
|
|
4
|
+
* Bridge to ruvector-hyperbolic-hnsw-wasm for hierarchical data representation
|
|
5
|
+
* using Poincaré ball model, Lorentz model, or Klein model.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { WasmBridge, WasmModuleStatus, HyperbolicConfig } from '../types.js';
|
|
9
|
+
import { HyperbolicConfigSchema } from '../types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Hyperbolic point
|
|
13
|
+
*/
|
|
14
|
+
export interface HyperbolicPoint {
|
|
15
|
+
coordinates: Float32Array;
|
|
16
|
+
curvature: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hyperbolic WASM module interface
|
|
21
|
+
*/
|
|
22
|
+
interface HyperbolicModule {
|
|
23
|
+
// Embedding operations
|
|
24
|
+
embed(vector: Float32Array, config: HyperbolicConfig): HyperbolicPoint;
|
|
25
|
+
project(point: HyperbolicPoint): Float32Array;
|
|
26
|
+
|
|
27
|
+
// Distance and similarity
|
|
28
|
+
distance(a: HyperbolicPoint, b: HyperbolicPoint): number;
|
|
29
|
+
similarity(a: HyperbolicPoint, b: HyperbolicPoint): number;
|
|
30
|
+
|
|
31
|
+
// Geometric operations
|
|
32
|
+
midpoint(a: HyperbolicPoint, b: HyperbolicPoint): HyperbolicPoint;
|
|
33
|
+
geodesic(a: HyperbolicPoint, b: HyperbolicPoint, steps: number): HyperbolicPoint[];
|
|
34
|
+
|
|
35
|
+
// Hierarchy operations
|
|
36
|
+
isAncestor(parent: HyperbolicPoint, child: HyperbolicPoint, threshold: number): boolean;
|
|
37
|
+
hierarchyDepth(point: HyperbolicPoint): number;
|
|
38
|
+
|
|
39
|
+
// HNSW search in hyperbolic space
|
|
40
|
+
addToIndex(id: string, point: HyperbolicPoint): void;
|
|
41
|
+
search(query: HyperbolicPoint, k: number): Array<{ id: string; distance: number }>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Hyperbolic Embeddings Bridge implementation
|
|
46
|
+
*/
|
|
47
|
+
export class HyperbolicBridge implements WasmBridge<HyperbolicModule> {
|
|
48
|
+
readonly name = 'ruvector-hyperbolic-hnsw-wasm';
|
|
49
|
+
readonly version = '0.1.0';
|
|
50
|
+
|
|
51
|
+
private _status: WasmModuleStatus = 'unloaded';
|
|
52
|
+
private _module: HyperbolicModule | null = null;
|
|
53
|
+
private config: HyperbolicConfig;
|
|
54
|
+
|
|
55
|
+
constructor(config?: Partial<HyperbolicConfig>) {
|
|
56
|
+
this.config = HyperbolicConfigSchema.parse(config ?? {});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get status(): WasmModuleStatus {
|
|
60
|
+
return this._status;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async init(): Promise<void> {
|
|
64
|
+
if (this._status === 'ready') return;
|
|
65
|
+
if (this._status === 'loading') return;
|
|
66
|
+
|
|
67
|
+
this._status = 'loading';
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const wasmModule = await import('@ruvector/hyperbolic-hnsw-wasm').catch(() => null);
|
|
71
|
+
|
|
72
|
+
if (wasmModule) {
|
|
73
|
+
this._module = wasmModule as unknown as HyperbolicModule;
|
|
74
|
+
} else {
|
|
75
|
+
this._module = this.createMockModule();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this._status = 'ready';
|
|
79
|
+
} catch (error) {
|
|
80
|
+
this._status = 'error';
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async destroy(): Promise<void> {
|
|
86
|
+
this._module = null;
|
|
87
|
+
this._status = 'unloaded';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
isReady(): boolean {
|
|
91
|
+
return this._status === 'ready';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getModule(): HyperbolicModule | null {
|
|
95
|
+
return this._module;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Embed a vector into hyperbolic space
|
|
100
|
+
*/
|
|
101
|
+
embed(vector: Float32Array, config?: Partial<HyperbolicConfig>): HyperbolicPoint {
|
|
102
|
+
if (!this._module) throw new Error('Hyperbolic module not initialized');
|
|
103
|
+
const mergedConfig = { ...this.config, ...config };
|
|
104
|
+
return this._module.embed(vector, mergedConfig);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Project hyperbolic point to Euclidean space
|
|
109
|
+
*/
|
|
110
|
+
project(point: HyperbolicPoint): Float32Array {
|
|
111
|
+
if (!this._module) throw new Error('Hyperbolic module not initialized');
|
|
112
|
+
return this._module.project(point);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Compute hyperbolic distance
|
|
117
|
+
*/
|
|
118
|
+
distance(a: HyperbolicPoint, b: HyperbolicPoint): number {
|
|
119
|
+
if (!this._module) throw new Error('Hyperbolic module not initialized');
|
|
120
|
+
return this._module.distance(a, b);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Compute hyperbolic similarity
|
|
125
|
+
*/
|
|
126
|
+
similarity(a: HyperbolicPoint, b: HyperbolicPoint): number {
|
|
127
|
+
if (!this._module) throw new Error('Hyperbolic module not initialized');
|
|
128
|
+
return this._module.similarity(a, b);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Check if one point is ancestor of another (hierarchy)
|
|
133
|
+
*/
|
|
134
|
+
isAncestor(parent: HyperbolicPoint, child: HyperbolicPoint, threshold = 0.1): boolean {
|
|
135
|
+
if (!this._module) throw new Error('Hyperbolic module not initialized');
|
|
136
|
+
return this._module.isAncestor(parent, child, threshold);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get hierarchy depth of a point
|
|
141
|
+
*/
|
|
142
|
+
hierarchyDepth(point: HyperbolicPoint): number {
|
|
143
|
+
if (!this._module) throw new Error('Hyperbolic module not initialized');
|
|
144
|
+
return this._module.hierarchyDepth(point);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Add point to HNSW index
|
|
149
|
+
*/
|
|
150
|
+
addToIndex(id: string, point: HyperbolicPoint): void {
|
|
151
|
+
if (!this._module) throw new Error('Hyperbolic module not initialized');
|
|
152
|
+
this._module.addToIndex(id, point);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Search in hyperbolic HNSW index
|
|
157
|
+
*/
|
|
158
|
+
search(query: HyperbolicPoint, k: number): Array<{ id: string; distance: number }> {
|
|
159
|
+
if (!this._module) throw new Error('Hyperbolic module not initialized');
|
|
160
|
+
return this._module.search(query, k);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create mock module for development
|
|
165
|
+
*/
|
|
166
|
+
private createMockModule(): HyperbolicModule {
|
|
167
|
+
const index = new Map<string, HyperbolicPoint>();
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
embed(vector: Float32Array, config: HyperbolicConfig): HyperbolicPoint {
|
|
171
|
+
// Map to Poincaré ball (simplified)
|
|
172
|
+
const norm = Math.sqrt(vector.reduce((s, v) => s + v * v, 0));
|
|
173
|
+
const scale = Math.tanh(norm) / (norm || 1);
|
|
174
|
+
const coords = new Float32Array(config.dimensions);
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < config.dimensions; i++) {
|
|
177
|
+
coords[i] = (vector[i % vector.length] || 0) * scale * 0.9;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { coordinates: coords, curvature: config.curvature };
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
project(point: HyperbolicPoint): Float32Array {
|
|
184
|
+
// Inverse of embedding
|
|
185
|
+
const coords = point.coordinates;
|
|
186
|
+
const norm = Math.sqrt(coords.reduce((s, v) => s + v * v, 0));
|
|
187
|
+
const scale = Math.atanh(Math.min(norm, 0.99)) / (norm || 1);
|
|
188
|
+
|
|
189
|
+
const result = new Float32Array(coords.length);
|
|
190
|
+
for (let i = 0; i < coords.length; i++) {
|
|
191
|
+
result[i] = coords[i] * scale;
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
distance(a: HyperbolicPoint, b: HyperbolicPoint): number {
|
|
197
|
+
// Poincaré distance approximation
|
|
198
|
+
const c = Math.abs(a.curvature);
|
|
199
|
+
const diffSq = a.coordinates.reduce((s, v, i) => s + Math.pow(v - b.coordinates[i], 2), 0);
|
|
200
|
+
const normA = a.coordinates.reduce((s, v) => s + v * v, 0);
|
|
201
|
+
const normB = b.coordinates.reduce((s, v) => s + v * v, 0);
|
|
202
|
+
|
|
203
|
+
const delta = 2 * diffSq / ((1 - normA) * (1 - normB));
|
|
204
|
+
return (1 / Math.sqrt(c)) * Math.acosh(1 + delta);
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
similarity(a: HyperbolicPoint, b: HyperbolicPoint): number {
|
|
208
|
+
const dist = this.distance(a, b);
|
|
209
|
+
return Math.exp(-dist);
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
midpoint(a: HyperbolicPoint, b: HyperbolicPoint): HyperbolicPoint {
|
|
213
|
+
const mid = new Float32Array(a.coordinates.length);
|
|
214
|
+
for (let i = 0; i < mid.length; i++) {
|
|
215
|
+
mid[i] = (a.coordinates[i] + b.coordinates[i]) / 2;
|
|
216
|
+
}
|
|
217
|
+
return { coordinates: mid, curvature: a.curvature };
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
geodesic(a: HyperbolicPoint, b: HyperbolicPoint, steps: number): HyperbolicPoint[] {
|
|
221
|
+
const result: HyperbolicPoint[] = [];
|
|
222
|
+
for (let i = 0; i <= steps; i++) {
|
|
223
|
+
const t = i / steps;
|
|
224
|
+
const coords = new Float32Array(a.coordinates.length);
|
|
225
|
+
for (let j = 0; j < coords.length; j++) {
|
|
226
|
+
coords[j] = a.coordinates[j] * (1 - t) + b.coordinates[j] * t;
|
|
227
|
+
}
|
|
228
|
+
result.push({ coordinates: coords, curvature: a.curvature });
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
isAncestor(parent: HyperbolicPoint, child: HyperbolicPoint, threshold: number): boolean {
|
|
234
|
+
const parentNorm = Math.sqrt(parent.coordinates.reduce((s, v) => s + v * v, 0));
|
|
235
|
+
const childNorm = Math.sqrt(child.coordinates.reduce((s, v) => s + v * v, 0));
|
|
236
|
+
return parentNorm < childNorm - threshold;
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
hierarchyDepth(point: HyperbolicPoint): number {
|
|
240
|
+
const norm = Math.sqrt(point.coordinates.reduce((s, v) => s + v * v, 0));
|
|
241
|
+
return Math.atanh(Math.min(norm, 0.99));
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
addToIndex(id: string, point: HyperbolicPoint): void {
|
|
245
|
+
index.set(id, point);
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
search(query: HyperbolicPoint, k: number): Array<{ id: string; distance: number }> {
|
|
249
|
+
const results: Array<{ id: string; distance: number }> = [];
|
|
250
|
+
|
|
251
|
+
for (const [id, point] of index) {
|
|
252
|
+
const distance = this.distance(query, point);
|
|
253
|
+
results.push({ id, distance });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
results.sort((a, b) => a.distance - b.distance);
|
|
257
|
+
return results.slice(0, k);
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Create a new hyperbolic bridge
|
|
265
|
+
*/
|
|
266
|
+
export function createHyperbolicBridge(config?: Partial<HyperbolicConfig>): HyperbolicBridge {
|
|
267
|
+
return new HyperbolicBridge(config);
|
|
268
|
+
}
|