23f7624596
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled
558 lines
20 KiB
TypeScript
558 lines
20 KiB
TypeScript
/**
|
|
* RuVector PostgreSQL Bridge - Hyperbolic Embeddings Example
|
|
*
|
|
* This example demonstrates:
|
|
* - Embedding file tree structures in hyperbolic space
|
|
* - Embedding class hierarchies with Poincare ball model
|
|
* - Calculating hierarchy-aware distances
|
|
* - Comparing Euclidean vs hyperbolic representations
|
|
*
|
|
* Run with: npx ts-node examples/ruvector/hyperbolic-hierarchies.ts
|
|
*
|
|
* @module @claude-flow/plugins/examples/ruvector/hyperbolic-hierarchies
|
|
*/
|
|
|
|
import {
|
|
createRuVectorBridge,
|
|
type RuVectorBridge,
|
|
} from '../../src/integrations/ruvector/index.js';
|
|
|
|
import {
|
|
PoincareBall,
|
|
PoincareEmbedding,
|
|
type HyperbolicConfig,
|
|
type HierarchyNode,
|
|
} from '../../src/integrations/ruvector/hyperbolic.js';
|
|
|
|
// ============================================================================
|
|
// Configuration
|
|
// ============================================================================
|
|
|
|
const config = {
|
|
connection: {
|
|
host: process.env.POSTGRES_HOST || 'localhost',
|
|
port: parseInt(process.env.POSTGRES_PORT || '5432', 10),
|
|
database: process.env.POSTGRES_DB || 'vectors',
|
|
user: process.env.POSTGRES_USER || 'postgres',
|
|
password: process.env.POSTGRES_PASSWORD || 'postgres',
|
|
},
|
|
hyperbolicDim: 32,
|
|
curvature: -1.0, // Negative curvature for hyperbolic space
|
|
};
|
|
|
|
// ============================================================================
|
|
// Sample Hierarchical Data
|
|
// ============================================================================
|
|
|
|
/**
|
|
* File system tree structure.
|
|
*/
|
|
const fileTree: HierarchyNode = {
|
|
id: 'root',
|
|
name: '/',
|
|
children: [
|
|
{
|
|
id: 'src',
|
|
name: 'src',
|
|
children: [
|
|
{
|
|
id: 'components',
|
|
name: 'components',
|
|
children: [
|
|
{ id: 'button', name: 'Button.tsx', children: [] },
|
|
{ id: 'input', name: 'Input.tsx', children: [] },
|
|
{ id: 'modal', name: 'Modal.tsx', children: [] },
|
|
{
|
|
id: 'forms',
|
|
name: 'forms',
|
|
children: [
|
|
{ id: 'login-form', name: 'LoginForm.tsx', children: [] },
|
|
{ id: 'signup-form', name: 'SignupForm.tsx', children: [] },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'services',
|
|
name: 'services',
|
|
children: [
|
|
{ id: 'auth-service', name: 'auth.ts', children: [] },
|
|
{ id: 'api-service', name: 'api.ts', children: [] },
|
|
{ id: 'storage-service', name: 'storage.ts', children: [] },
|
|
],
|
|
},
|
|
{
|
|
id: 'utils',
|
|
name: 'utils',
|
|
children: [
|
|
{ id: 'format', name: 'format.ts', children: [] },
|
|
{ id: 'validate', name: 'validate.ts', children: [] },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'tests',
|
|
name: 'tests',
|
|
children: [
|
|
{ id: 'unit', name: 'unit', children: [
|
|
{ id: 'auth-test', name: 'auth.test.ts', children: [] },
|
|
{ id: 'api-test', name: 'api.test.ts', children: [] },
|
|
]},
|
|
{ id: 'integration', name: 'integration', children: [
|
|
{ id: 'e2e-test', name: 'e2e.test.ts', children: [] },
|
|
]},
|
|
],
|
|
},
|
|
{
|
|
id: 'config',
|
|
name: 'config',
|
|
children: [
|
|
{ id: 'tsconfig', name: 'tsconfig.json', children: [] },
|
|
{ id: 'eslint', name: '.eslintrc.js', children: [] },
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Class inheritance hierarchy (TypeScript/OOP).
|
|
*/
|
|
const classHierarchy: HierarchyNode = {
|
|
id: 'object',
|
|
name: 'Object',
|
|
children: [
|
|
{
|
|
id: 'error',
|
|
name: 'Error',
|
|
children: [
|
|
{
|
|
id: 'validation-error',
|
|
name: 'ValidationError',
|
|
children: [
|
|
{ id: 'field-error', name: 'FieldValidationError', children: [] },
|
|
{ id: 'schema-error', name: 'SchemaValidationError', children: [] },
|
|
],
|
|
},
|
|
{
|
|
id: 'http-error',
|
|
name: 'HttpError',
|
|
children: [
|
|
{ id: 'not-found', name: 'NotFoundError', children: [] },
|
|
{ id: 'unauthorized', name: 'UnauthorizedError', children: [] },
|
|
{ id: 'forbidden', name: 'ForbiddenError', children: [] },
|
|
],
|
|
},
|
|
{ id: 'database-error', name: 'DatabaseError', children: [] },
|
|
],
|
|
},
|
|
{
|
|
id: 'base-service',
|
|
name: 'BaseService',
|
|
children: [
|
|
{
|
|
id: 'crud-service',
|
|
name: 'CrudService',
|
|
children: [
|
|
{ id: 'user-service', name: 'UserService', children: [] },
|
|
{ id: 'product-service', name: 'ProductService', children: [] },
|
|
],
|
|
},
|
|
{ id: 'auth-svc', name: 'AuthService', children: [] },
|
|
{ id: 'cache-svc', name: 'CacheService', children: [] },
|
|
],
|
|
},
|
|
{
|
|
id: 'base-controller',
|
|
name: 'BaseController',
|
|
children: [
|
|
{ id: 'user-controller', name: 'UserController', children: [] },
|
|
{ id: 'auth-controller', name: 'AuthController', children: [] },
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
// ============================================================================
|
|
// Helper Functions
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Flatten hierarchy to list with depth information.
|
|
*/
|
|
function flattenHierarchy(
|
|
node: HierarchyNode,
|
|
depth: number = 0,
|
|
parent: string | null = null
|
|
): Array<{ node: HierarchyNode; depth: number; parent: string | null }> {
|
|
const result: Array<{ node: HierarchyNode; depth: number; parent: string | null }> = [
|
|
{ node, depth, parent },
|
|
];
|
|
|
|
for (const child of node.children) {
|
|
result.push(...flattenHierarchy(child, depth + 1, node.id));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Find path between two nodes in hierarchy.
|
|
*/
|
|
function findPath(root: HierarchyNode, targetId: string): string[] | null {
|
|
if (root.id === targetId) return [root.id];
|
|
|
|
for (const child of root.children) {
|
|
const childPath = findPath(child, targetId);
|
|
if (childPath) return [root.id, ...childPath];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Calculate tree distance (number of edges in path).
|
|
*/
|
|
function treeDistance(root: HierarchyNode, id1: string, id2: string): number {
|
|
const path1 = findPath(root, id1) || [];
|
|
const path2 = findPath(root, id2) || [];
|
|
|
|
// Find lowest common ancestor
|
|
let lcaDepth = 0;
|
|
while (
|
|
lcaDepth < path1.length &&
|
|
lcaDepth < path2.length &&
|
|
path1[lcaDepth] === path2[lcaDepth]
|
|
) {
|
|
lcaDepth++;
|
|
}
|
|
|
|
// Distance = path from node1 to LCA + path from LCA to node2
|
|
return (path1.length - lcaDepth) + (path2.length - lcaDepth);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Main Example
|
|
// ============================================================================
|
|
|
|
async function main(): Promise<void> {
|
|
console.log('RuVector PostgreSQL Bridge - Hyperbolic Embeddings Example');
|
|
console.log('============================================================\n');
|
|
|
|
const bridge: RuVectorBridge = createRuVectorBridge({
|
|
connectionString: `postgresql://${config.connection.user}:${config.connection.password}@${config.connection.host}:${config.connection.port}/${config.connection.database}`,
|
|
});
|
|
|
|
// Initialize Poincare ball model
|
|
const poincare = new PoincareBall({
|
|
dimension: config.hyperbolicDim,
|
|
curvature: config.curvature,
|
|
epsilon: 1e-6,
|
|
});
|
|
|
|
try {
|
|
await bridge.connect();
|
|
console.log('Connected to PostgreSQL\n');
|
|
|
|
// ========================================================================
|
|
// 1. Embed File Tree Structure
|
|
// ========================================================================
|
|
console.log('1. Embedding File Tree in Hyperbolic Space');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const fileEmbedding = new PoincareEmbedding({
|
|
dimension: config.hyperbolicDim,
|
|
curvature: config.curvature,
|
|
learningRate: 0.01,
|
|
});
|
|
|
|
// Train embeddings on file hierarchy
|
|
console.log(' Training Poincare embeddings for file tree...');
|
|
const startTrain = performance.now();
|
|
await fileEmbedding.train(fileTree, { epochs: 100, batchSize: 16 });
|
|
const trainTime = performance.now() - startTrain;
|
|
console.log(` Training completed in ${trainTime.toFixed(0)}ms`);
|
|
|
|
// Get embeddings for all nodes
|
|
const flatFiles = flattenHierarchy(fileTree);
|
|
console.log(`\n Embedded ${flatFiles.length} nodes`);
|
|
|
|
// Show embedding norms (closer to 1 = deeper in hierarchy)
|
|
console.log('\n Embedding norms by depth (closer to 1 = deeper):');
|
|
const depthGroups = new Map<number, Array<{ name: string; norm: number }>>();
|
|
|
|
for (const { node, depth } of flatFiles) {
|
|
const embedding = fileEmbedding.getEmbedding(node.id);
|
|
if (embedding) {
|
|
const norm = Math.sqrt(embedding.reduce((s, v) => s + v * v, 0));
|
|
if (!depthGroups.has(depth)) depthGroups.set(depth, []);
|
|
depthGroups.get(depth)?.push({ name: node.name, norm });
|
|
}
|
|
}
|
|
|
|
depthGroups.forEach((nodes, depth) => {
|
|
const avgNorm = nodes.reduce((s, n) => s + n.norm, 0) / nodes.length;
|
|
const samples = nodes.slice(0, 3).map(n => n.name).join(', ');
|
|
console.log(` Depth ${depth}: avg norm = ${avgNorm.toFixed(4)} (${samples}${nodes.length > 3 ? '...' : ''})`);
|
|
});
|
|
console.log();
|
|
|
|
// ========================================================================
|
|
// 2. Hyperbolic Distance vs Tree Distance
|
|
// ========================================================================
|
|
console.log('2. Comparing Hyperbolic Distance to Tree Distance');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const testPairs = [
|
|
['button', 'input'], // Same directory
|
|
['button', 'login-form'], // Nearby (components)
|
|
['button', 'auth-service'], // Different subtrees
|
|
['button', 'auth-test'], // Far apart (src vs tests)
|
|
['root', 'login-form'], // Root to deep node
|
|
];
|
|
|
|
console.log(' Node Pair | Tree Dist | Hyperbolic Dist | Correlation');
|
|
console.log(' ' + '-'.repeat(75));
|
|
|
|
for (const [id1, id2] of testPairs) {
|
|
const treeDist = treeDistance(fileTree, id1, id2);
|
|
const emb1 = fileEmbedding.getEmbedding(id1);
|
|
const emb2 = fileEmbedding.getEmbedding(id2);
|
|
|
|
if (emb1 && emb2) {
|
|
const hypDist = poincare.distance(emb1, emb2);
|
|
const node1 = flatFiles.find(f => f.node.id === id1)?.node.name || id1;
|
|
const node2 = flatFiles.find(f => f.node.id === id2)?.node.name || id2;
|
|
const pairName = `${node1} <-> ${node2}`;
|
|
|
|
console.log(
|
|
` ${pairName.padEnd(25)} | ${treeDist.toString().padStart(9)} | ` +
|
|
`${hypDist.toFixed(4).padStart(15)} | ` +
|
|
`${treeDist > 0 ? (hypDist / treeDist).toFixed(3) : 'N/A'}`
|
|
);
|
|
}
|
|
}
|
|
console.log();
|
|
|
|
// ========================================================================
|
|
// 3. Embed Class Hierarchy
|
|
// ========================================================================
|
|
console.log('3. Embedding Class Inheritance Hierarchy');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const classEmbedding = new PoincareEmbedding({
|
|
dimension: config.hyperbolicDim,
|
|
curvature: config.curvature,
|
|
learningRate: 0.01,
|
|
});
|
|
|
|
console.log(' Training Poincare embeddings for class hierarchy...');
|
|
await classEmbedding.train(classHierarchy, { epochs: 100, batchSize: 16 });
|
|
|
|
const flatClasses = flattenHierarchy(classHierarchy);
|
|
console.log(` Embedded ${flatClasses.length} classes`);
|
|
|
|
// Show class hierarchy with embeddings
|
|
console.log('\n Class hierarchy with embedding norms:');
|
|
for (const { node, depth } of flatClasses) {
|
|
const emb = classEmbedding.getEmbedding(node.id);
|
|
const norm = emb ? Math.sqrt(emb.reduce((s, v) => s + v * v, 0)) : 0;
|
|
const indent = ' '.repeat(depth);
|
|
console.log(` ${indent}${node.name} (norm: ${norm.toFixed(4)})`);
|
|
}
|
|
console.log();
|
|
|
|
// ========================================================================
|
|
// 4. Find Nearest Ancestors and Descendants
|
|
// ========================================================================
|
|
console.log('4. Finding Nearest Ancestors and Descendants');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const queryClass = 'not-found'; // NotFoundError
|
|
const queryEmb = classEmbedding.getEmbedding(queryClass);
|
|
|
|
if (queryEmb) {
|
|
// Find classes by hyperbolic distance
|
|
const distances = flatClasses
|
|
.filter(c => c.node.id !== queryClass)
|
|
.map(({ node }) => {
|
|
const emb = classEmbedding.getEmbedding(node.id);
|
|
if (!emb) return null;
|
|
return {
|
|
id: node.id,
|
|
name: node.name,
|
|
distance: poincare.distance(queryEmb, emb),
|
|
isAncestor: findPath(classHierarchy, queryClass)?.includes(node.id) ?? false,
|
|
};
|
|
})
|
|
.filter((d): d is NonNullable<typeof d> => d !== null)
|
|
.sort((a, b) => a.distance - b.distance);
|
|
|
|
console.log(` Query: ${queryClass} (NotFoundError)`);
|
|
console.log('\n Nearest by hyperbolic distance:');
|
|
distances.slice(0, 5).forEach((d, i) => {
|
|
const relation = d.isAncestor ? '[ancestor]' : '';
|
|
console.log(` ${i + 1}. ${d.name} - distance: ${d.distance.toFixed(4)} ${relation}`);
|
|
});
|
|
|
|
console.log('\n Actual ancestors (by tree structure):');
|
|
const ancestors = findPath(classHierarchy, queryClass) || [];
|
|
ancestors.slice(0, -1).forEach((id, i) => {
|
|
const node = flatClasses.find(c => c.node.id === id);
|
|
if (node) {
|
|
const emb = classEmbedding.getEmbedding(id);
|
|
const dist = emb ? poincare.distance(queryEmb, emb) : 0;
|
|
console.log(` ${i + 1}. ${node.node.name} - distance: ${dist.toFixed(4)}`);
|
|
}
|
|
});
|
|
}
|
|
console.log();
|
|
|
|
// ========================================================================
|
|
// 5. Hyperbolic Operations
|
|
// ========================================================================
|
|
console.log('5. Hyperbolic Space Operations');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const emb1 = classEmbedding.getEmbedding('error');
|
|
const emb2 = classEmbedding.getEmbedding('validation-error');
|
|
|
|
if (emb1 && emb2) {
|
|
// Hyperbolic midpoint (Mobius gyromidpoint)
|
|
const midpoint = poincare.mobius_add(
|
|
poincare.scalar_mult(0.5, emb1),
|
|
poincare.scalar_mult(0.5, emb2)
|
|
);
|
|
const midNorm = Math.sqrt(midpoint.reduce((s, v) => s + v * v, 0));
|
|
|
|
console.log(' Midpoint between Error and ValidationError:');
|
|
console.log(` Norm of midpoint: ${midNorm.toFixed(4)}`);
|
|
console.log(` Distance to Error: ${poincare.distance(midpoint, emb1).toFixed(4)}`);
|
|
console.log(` Distance to ValidationError: ${poincare.distance(midpoint, emb2).toFixed(4)}`);
|
|
|
|
// Exponential and logarithmic maps
|
|
console.log('\n Exponential map (tangent space -> hyperbolic):');
|
|
const tangentVector = Array.from({ length: config.hyperbolicDim }, () => Math.random() * 0.1);
|
|
const mapped = poincare.exp_map(tangentVector, emb1);
|
|
const mappedNorm = Math.sqrt(mapped.reduce((s, v) => s + v * v, 0));
|
|
console.log(` Input tangent vector norm: ${Math.sqrt(tangentVector.reduce((s, v) => s + v * v, 0)).toFixed(4)}`);
|
|
console.log(` Mapped point norm: ${mappedNorm.toFixed(4)}`);
|
|
}
|
|
console.log();
|
|
|
|
// ========================================================================
|
|
// 6. Store in PostgreSQL with Hyperbolic Distance
|
|
// ========================================================================
|
|
console.log('6. Storing Hyperbolic Embeddings in PostgreSQL');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
// Create collection for class embeddings
|
|
await bridge.createCollection('class_hierarchy_embeddings', {
|
|
dimensions: config.hyperbolicDim,
|
|
distanceMetric: 'euclidean', // Use Euclidean for storage, compute hyperbolic distance separately
|
|
indexType: 'hnsw',
|
|
});
|
|
|
|
// Store embeddings
|
|
for (const { node, depth, parent } of flatClasses) {
|
|
const emb = classEmbedding.getEmbedding(node.id);
|
|
if (emb) {
|
|
await bridge.insert('class_hierarchy_embeddings', {
|
|
id: node.id,
|
|
embedding: emb,
|
|
metadata: {
|
|
name: node.name,
|
|
depth,
|
|
parent,
|
|
isLeaf: node.children.length === 0,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log(` Stored ${flatClasses.length} hyperbolic embeddings`);
|
|
|
|
// Query and re-rank with hyperbolic distance
|
|
const queryId = 'unauthorized';
|
|
const queryHypEmb = classEmbedding.getEmbedding(queryId);
|
|
|
|
if (queryHypEmb) {
|
|
// Get candidates using Euclidean distance (fast approximation)
|
|
const candidates = await bridge.search('class_hierarchy_embeddings', queryHypEmb, {
|
|
k: 10,
|
|
includeMetadata: true,
|
|
});
|
|
|
|
// Re-rank with hyperbolic distance
|
|
const reranked = candidates
|
|
.map(c => {
|
|
const hypDist = poincare.distance(queryHypEmb, c.embedding);
|
|
return { ...c, hyperbolicDistance: hypDist };
|
|
})
|
|
.sort((a, b) => a.hyperbolicDistance - b.hyperbolicDistance);
|
|
|
|
console.log(`\n Query: ${queryId} (UnauthorizedError)`);
|
|
console.log(' Results re-ranked by hyperbolic distance:');
|
|
reranked.slice(0, 5).forEach((r, i) => {
|
|
console.log(` ${i + 1}. ${r.metadata?.name} (hyp dist: ${r.hyperbolicDistance.toFixed(4)})`);
|
|
});
|
|
}
|
|
|
|
// ========================================================================
|
|
// 7. Euclidean vs Hyperbolic Comparison
|
|
// ========================================================================
|
|
console.log('\n7. Euclidean vs Hyperbolic Distance Comparison');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
// Compare how well each distance metric preserves hierarchy
|
|
let euclideanCorrelation = 0;
|
|
let hyperbolicCorrelation = 0;
|
|
let comparisons = 0;
|
|
|
|
for (const { node: node1 } of flatClasses) {
|
|
for (const { node: node2 } of flatClasses) {
|
|
if (node1.id >= node2.id) continue;
|
|
|
|
const emb1 = classEmbedding.getEmbedding(node1.id);
|
|
const emb2 = classEmbedding.getEmbedding(node2.id);
|
|
if (!emb1 || !emb2) continue;
|
|
|
|
const treeDist = treeDistance(classHierarchy, node1.id, node2.id);
|
|
const eucDist = Math.sqrt(emb1.reduce((s, v, i) => s + Math.pow(v - emb2[i], 2), 0));
|
|
const hypDist = poincare.distance(emb1, emb2);
|
|
|
|
// Spearman-like correlation (rank agreement)
|
|
if (treeDist > 0) {
|
|
euclideanCorrelation += eucDist / treeDist;
|
|
hyperbolicCorrelation += hypDist / treeDist;
|
|
comparisons++;
|
|
}
|
|
}
|
|
}
|
|
|
|
euclideanCorrelation /= comparisons;
|
|
hyperbolicCorrelation /= comparisons;
|
|
|
|
console.log(' Distance metric quality (lower = better preserves tree structure):');
|
|
console.log(` Euclidean: ${euclideanCorrelation.toFixed(4)}`);
|
|
console.log(` Hyperbolic: ${hyperbolicCorrelation.toFixed(4)}`);
|
|
console.log(` Improvement: ${((euclideanCorrelation / hyperbolicCorrelation - 1) * 100).toFixed(1)}%`);
|
|
|
|
// ========================================================================
|
|
// Done
|
|
// ========================================================================
|
|
console.log('\n' + '='.repeat(60));
|
|
console.log('Hyperbolic embeddings example completed!');
|
|
console.log('='.repeat(60));
|
|
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
throw error;
|
|
} finally {
|
|
await bridge.disconnect();
|
|
console.log('\nDisconnected from PostgreSQL.');
|
|
}
|
|
}
|
|
|
|
main().catch(console.error);
|