npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ruvector/gnn

v0.1.22

Published

Graph Neural Network capabilities for Ruvector - Node.js bindings

Readme

@ruvector/gnn - Graph Neural Network Node.js Bindings

High-performance Graph Neural Network (GNN) capabilities for Ruvector, powered by Rust and NAPI-RS.

npm version CI

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

npm install @ruvector/gnn

Quick Start

Creating a GNN Layer

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

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

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

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

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

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

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

function differentiableSearch(
  query: number[],
  candidateEmbeddings: number[][],
  k: number,
  temperature: number
): { indices: number[], weights: number[] }

hierarchicalForward

function hierarchicalForward(
  query: number[],
  layerEmbeddings: number[][][],
  gnnLayersJson: string[]
): number[]

Utility Functions

getCompressionLevel

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

# 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