Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
252
vendor/ruvector/crates/ruvector-gnn-node/README.md
vendored
Normal file
252
vendor/ruvector/crates/ruvector-gnn-node/README.md
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
# @ruvector/gnn - Graph Neural Network Node.js Bindings
|
||||
|
||||
High-performance Graph Neural Network (GNN) capabilities for Ruvector, powered by Rust and NAPI-RS.
|
||||
|
||||
[](https://www.npmjs.com/package/@ruvector/gnn)
|
||||
[](https://github.com/ruvnet/ruvector/actions/workflows/build-gnn.yml)
|
||||
|
||||
## Features
|
||||
|
||||
- **GNN Layers**: Multi-head attention, layer normalization, GRU cells
|
||||
- **Tensor Compression**: Adaptive compression with 5 levels (None, Half, PQ8, PQ4, Binary)
|
||||
- **Differentiable Search**: Soft attention-based search with temperature scaling
|
||||
- **Hierarchical Processing**: Multi-layer GNN forward pass
|
||||
- **Zero-copy**: Efficient data transfer between JavaScript and Rust
|
||||
- **TypeScript Support**: Full type definitions included
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @ruvector/gnn
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Creating a GNN Layer
|
||||
|
||||
```javascript
|
||||
const { RuvectorLayer } = require('@ruvector/gnn');
|
||||
|
||||
// Create a GNN layer with:
|
||||
// - Input dimension: 128
|
||||
// - Hidden dimension: 256
|
||||
// - Attention heads: 4
|
||||
// - Dropout rate: 0.1
|
||||
const layer = new RuvectorLayer(128, 256, 4, 0.1);
|
||||
|
||||
// Forward pass
|
||||
const nodeEmbedding = new Array(128).fill(0).map(() => Math.random());
|
||||
const neighborEmbeddings = [
|
||||
new Array(128).fill(0).map(() => Math.random()),
|
||||
new Array(128).fill(0).map(() => Math.random()),
|
||||
];
|
||||
const edgeWeights = [0.7, 0.3];
|
||||
|
||||
const output = layer.forward(nodeEmbedding, neighborEmbeddings, edgeWeights);
|
||||
console.log('Output dimension:', output.length); // 256
|
||||
```
|
||||
|
||||
### Tensor Compression
|
||||
|
||||
```javascript
|
||||
const { TensorCompress, getCompressionLevel } = require('@ruvector/gnn');
|
||||
|
||||
const compressor = new TensorCompress();
|
||||
const embedding = new Array(128).fill(0).map(() => Math.random());
|
||||
|
||||
// Adaptive compression based on access frequency
|
||||
const accessFreq = 0.5; // 50% access rate
|
||||
console.log('Selected level:', getCompressionLevel(accessFreq)); // "half"
|
||||
|
||||
const compressed = compressor.compress(embedding, accessFreq);
|
||||
const decompressed = compressor.decompress(compressed);
|
||||
|
||||
console.log('Original size:', embedding.length);
|
||||
console.log('Compression ratio:', compressed.length / JSON.stringify(embedding).length);
|
||||
|
||||
// Explicit compression level
|
||||
const level = {
|
||||
level_type: 'pq8',
|
||||
subvectors: 8,
|
||||
centroids: 16
|
||||
};
|
||||
const compressedPQ = compressor.compressWithLevel(embedding, level);
|
||||
```
|
||||
|
||||
### Differentiable Search
|
||||
|
||||
```javascript
|
||||
const { differentiableSearch } = require('@ruvector/gnn');
|
||||
|
||||
const query = [1.0, 0.0, 0.0];
|
||||
const candidates = [
|
||||
[1.0, 0.0, 0.0], // Perfect match
|
||||
[0.9, 0.1, 0.0], // Close match
|
||||
[0.0, 1.0, 0.0], // Orthogonal
|
||||
];
|
||||
|
||||
const result = differentiableSearch(query, candidates, 2, 1.0);
|
||||
console.log('Top-2 indices:', result.indices); // [0, 1]
|
||||
console.log('Soft weights:', result.weights); // [0.x, 0.y]
|
||||
```
|
||||
|
||||
### Hierarchical Forward Pass
|
||||
|
||||
```javascript
|
||||
const { hierarchicalForward, RuvectorLayer } = require('@ruvector/gnn');
|
||||
|
||||
const query = [1.0, 0.0];
|
||||
|
||||
// Layer embeddings (organized by HNSW layers)
|
||||
const layerEmbeddings = [
|
||||
[[1.0, 0.0], [0.0, 1.0]], // Layer 0 embeddings
|
||||
];
|
||||
|
||||
// Create and serialize GNN layers
|
||||
const layer1 = new RuvectorLayer(2, 2, 1, 0.0);
|
||||
const layers = [layer1.toJson()];
|
||||
|
||||
// Hierarchical processing
|
||||
const result = hierarchicalForward(query, layerEmbeddings, layers);
|
||||
console.log('Final embedding:', result);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### RuvectorLayer
|
||||
|
||||
#### Constructor
|
||||
|
||||
```typescript
|
||||
new RuvectorLayer(
|
||||
inputDim: number,
|
||||
hiddenDim: number,
|
||||
heads: number,
|
||||
dropout: number
|
||||
): RuvectorLayer
|
||||
```
|
||||
|
||||
#### Methods
|
||||
|
||||
- `forward(nodeEmbedding: number[], neighborEmbeddings: number[][], edgeWeights: number[]): number[]`
|
||||
- `toJson(): string` - Serialize layer to JSON
|
||||
- `fromJson(json: string): RuvectorLayer` - Deserialize layer from JSON
|
||||
|
||||
### TensorCompress
|
||||
|
||||
#### Constructor
|
||||
|
||||
```typescript
|
||||
new TensorCompress(): TensorCompress
|
||||
```
|
||||
|
||||
#### Methods
|
||||
|
||||
- `compress(embedding: number[], accessFreq: number): string` - Adaptive compression
|
||||
- `compressWithLevel(embedding: number[], level: CompressionLevelConfig): string` - Explicit level
|
||||
- `decompress(compressedJson: string): number[]` - Decompress tensor
|
||||
|
||||
#### CompressionLevelConfig
|
||||
|
||||
```typescript
|
||||
interface CompressionLevelConfig {
|
||||
level_type: 'none' | 'half' | 'pq8' | 'pq4' | 'binary';
|
||||
scale?: number; // For 'half'
|
||||
subvectors?: number; // For 'pq8', 'pq4'
|
||||
centroids?: number; // For 'pq8'
|
||||
outlier_threshold?: number; // For 'pq4'
|
||||
threshold?: number; // For 'binary'
|
||||
}
|
||||
```
|
||||
|
||||
### Search Functions
|
||||
|
||||
#### differentiableSearch
|
||||
|
||||
```typescript
|
||||
function differentiableSearch(
|
||||
query: number[],
|
||||
candidateEmbeddings: number[][],
|
||||
k: number,
|
||||
temperature: number
|
||||
): { indices: number[], weights: number[] }
|
||||
```
|
||||
|
||||
#### hierarchicalForward
|
||||
|
||||
```typescript
|
||||
function hierarchicalForward(
|
||||
query: number[],
|
||||
layerEmbeddings: number[][][],
|
||||
gnnLayersJson: string[]
|
||||
): number[]
|
||||
```
|
||||
|
||||
### Utility Functions
|
||||
|
||||
#### getCompressionLevel
|
||||
|
||||
```typescript
|
||||
function getCompressionLevel(accessFreq: number): string
|
||||
```
|
||||
|
||||
Returns the compression level that would be selected for the given access frequency:
|
||||
- `accessFreq > 0.8`: "none" (hot data)
|
||||
- `accessFreq > 0.4`: "half" (warm data)
|
||||
- `accessFreq > 0.1`: "pq8" (cool data)
|
||||
- `accessFreq > 0.01`: "pq4" (cold data)
|
||||
- `accessFreq <= 0.01`: "binary" (archive)
|
||||
|
||||
## Compression Levels
|
||||
|
||||
### None
|
||||
Full precision, no compression. Best for frequently accessed data.
|
||||
|
||||
### Half Precision
|
||||
~50% space savings with minimal quality loss. Good for warm data.
|
||||
|
||||
### PQ8 (8-bit Product Quantization)
|
||||
~8x compression using 8-bit codes. Suitable for cool data.
|
||||
|
||||
### PQ4 (4-bit Product Quantization)
|
||||
~16x compression with outlier handling. For cold data.
|
||||
|
||||
### Binary
|
||||
~32x compression, values become +1/-1. For archival data.
|
||||
|
||||
## Performance
|
||||
|
||||
- **Zero-copy operations** where possible
|
||||
- **SIMD optimizations** for vector operations
|
||||
- **Parallel processing** with Rayon
|
||||
- **Native performance** with Rust backend
|
||||
|
||||
## Building from Source
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Build debug
|
||||
npm run build:debug
|
||||
|
||||
# Build release
|
||||
npm run build
|
||||
|
||||
# Run tests
|
||||
npm test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT - See LICENSE file for details
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please see the main Ruvector repository for guidelines.
|
||||
|
||||
## Links
|
||||
|
||||
- [GitHub Repository](https://github.com/ruvnet/ruvector)
|
||||
- [Documentation](https://docs.ruvector.io)
|
||||
- [Issues](https://github.com/ruvnet/ruvector/issues)
|
||||
Reference in New Issue
Block a user