tasq/node_modules/agentdb/examples/browser/hyperbolic-hierarchy.html

498 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AgentDB - Hyperbolic Hierarchy Visualization</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #2d3561 0%, #c05c7e 100%);
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
header {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
margin-bottom: 30px;
}
h1 {
color: #2d3561;
margin-bottom: 10px;
}
.demo-section {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
h2 {
color: #333;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #2d3561;
}
#canvas {
width: 100%;
height: 600px;
background: #f8f9fa;
border-radius: 5px;
display: block;
}
.controls {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 20px;
}
button {
background: #2d3561;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: background 0.3s;
}
button:hover {
background: #1f2545;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin-top: 20px;
}
.info-card {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
border-left: 4px solid #2d3561;
}
.info-card h3 {
color: #2d3561;
font-size: 14px;
margin-bottom: 5px;
}
.info-card p {
color: #666;
font-size: 20px;
font-weight: bold;
}
.legend {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin-top: 20px;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 3px;
}
footer {
text-align: center;
color: white;
margin-top: 30px;
opacity: 0.9;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Hyperbolic Hierarchy Visualization</h1>
<p class="subtitle">Visualize hierarchical relationships in hyperbolic space using Poincaré ball model</p>
</header>
<div class="demo-section">
<h2>Interactive Visualization</h2>
<div class="controls">
<button id="loadTaxonomy">Load Animal Taxonomy</button>
<button id="loadOrgChart">Load Organization Chart</button>
<button id="loadFilesystem">Load File System</button>
<button id="computeDistances">Compute Hyperbolic Distances</button>
<button id="findNearest">Find Nearest Neighbors</button>
</div>
<canvas id="canvas"></canvas>
<div class="info-grid" id="stats">
<div class="info-card">
<h3>Total Nodes</h3>
<p id="nodeCount">0</p>
</div>
<div class="info-card">
<h3>Hierarchy Depth</h3>
<p id="depth">0</p>
</div>
<div class="info-card">
<h3>Avg Poincaré Distance</h3>
<p id="avgDistance">0.00</p>
</div>
<div class="info-card">
<h3>Computation Time</h3>
<p id="computeTime">0ms</p>
</div>
</div>
<div class="legend">
<div class="legend-item">
<div class="legend-color" style="background: #e74c3c;"></div>
<span>Root Level</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #3498db;"></div>
<span>Level 1</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #2ecc71;"></div>
<span>Level 2</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #f39c12;"></div>
<span>Level 3+</span>
</div>
</div>
</div>
<div class="demo-section">
<h2>About Hyperbolic Space</h2>
<p style="line-height: 1.8;">
Hyperbolic space provides a natural way to represent hierarchical structures.
Unlike Euclidean space, the Poincaré ball model allows exponentially more room
at the boundary, making it perfect for tree-like structures where the number
of nodes grows exponentially with depth.
</p>
<div class="info-grid" style="margin-top: 20px;">
<div class="info-card">
<h3>Poincaré Ball</h3>
<p style="font-size: 14px; font-weight: normal;">
All points lie within a unit circle. Distance grows exponentially near the boundary.
</p>
</div>
<div class="info-card">
<h3>Hierarchy Preservation</h3>
<p style="font-size: 14px; font-weight: normal;">
Parent-child relationships maintain consistent distances in hyperbolic space.
</p>
</div>
<div class="info-card">
<h3>Better Embeddings</h3>
<p style="font-size: 14px; font-weight: normal;">
Lower distortion when embedding trees compared to Euclidean space.
</p>
</div>
</div>
</div>
<footer>
<p>AgentDB v2.0 | Hyperbolic Attention powered by WASM</p>
</footer>
</div>
<script type="module">
import {
AttentionBrowser,
createAttention
} from '../../dist/agentdb.browser.js';
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let attention = null;
let currentHierarchy = null;
// Set canvas size
function resizeCanvas() {
canvas.width = canvas.clientWidth * window.devicePixelRatio;
canvas.height = canvas.clientHeight * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Initialize attention
async function initialize() {
attention = createAttention({
dimension: 64,
curvature: -1.0,
useWASM: true
});
await attention.initialize();
}
// Sample hierarchies
const hierarchies = {
taxonomy: {
name: 'Animal Kingdom',
children: [
{
name: 'Mammals',
children: [
{ name: 'Primates', children: [{ name: 'Humans' }, { name: 'Chimpanzees' }] },
{ name: 'Carnivores', children: [{ name: 'Cats' }, { name: 'Dogs' }] }
]
},
{
name: 'Birds',
children: [
{ name: 'Raptors', children: [{ name: 'Eagles' }, { name: 'Hawks' }] },
{ name: 'Songbirds', children: [{ name: 'Sparrows' }, { name: 'Robins' }] }
]
}
]
},
orgChart: {
name: 'CEO',
children: [
{
name: 'CTO',
children: [
{ name: 'Engineering', children: [{ name: 'Frontend' }, { name: 'Backend' }] },
{ name: 'DevOps', children: [{ name: 'Infrastructure' }] }
]
},
{
name: 'CFO',
children: [
{ name: 'Accounting', children: [{ name: 'Payroll' }, { name: 'Tax' }] }
]
}
]
},
filesystem: {
name: '/',
children: [
{
name: 'home',
children: [
{ name: 'user', children: [{ name: 'documents' }, { name: 'downloads' }] }
]
},
{
name: 'var',
children: [
{ name: 'log', children: [{ name: 'system.log' }] },
{ name: 'www', children: [{ name: 'html' }] }
]
}
]
}
};
// Convert hierarchy to embeddings
function hierarchyToEmbeddings(node, depth = 0, angle = 0, angleSpan = Math.PI * 2) {
const embeddings = [];
const dim = 64;
// Map to Poincaré disk
const radius = Math.min(0.95, depth * 0.3);
const x = radius * Math.cos(angle);
const y = radius * Math.sin(angle);
// Create embedding
const embedding = new Float32Array(dim);
embedding[0] = x;
embedding[1] = y;
for (let i = 2; i < dim; i++) {
embedding[i] = (Math.random() - 0.5) * 0.1;
}
embeddings.push({
name: node.name,
embedding,
depth,
x,
y
});
// Process children
if (node.children) {
const childAngleSpan = angleSpan / node.children.length;
node.children.forEach((child, i) => {
const childAngle = angle - angleSpan / 2 + childAngleSpan * (i + 0.5);
embeddings.push(...hierarchyToEmbeddings(child, depth + 1, childAngle, childAngleSpan));
});
}
return embeddings;
}
// Draw visualization
function drawVisualization(embeddings) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const centerX = width / 2;
const centerY = height / 2;
const radius = Math.min(width, height) * 0.4;
ctx.clearRect(0, 0, width, height);
// Draw Poincaré disk
ctx.strokeStyle = '#ddd';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.stroke();
// Draw grid
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
for (let r = radius / 4; r < radius; r += radius / 4) {
ctx.beginPath();
ctx.arc(centerX, centerY, r, 0, Math.PI * 2);
ctx.stroke();
}
// Draw nodes
const colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12'];
embeddings.forEach(node => {
const px = centerX + node.x * radius;
const py = centerY + node.y * radius;
// Draw node
ctx.fillStyle = colors[Math.min(node.depth, colors.length - 1)];
ctx.beginPath();
ctx.arc(px, py, 8, 0, Math.PI * 2);
ctx.fill();
// Draw label
ctx.fillStyle = '#333';
ctx.font = '12px sans-serif';
ctx.fillText(node.name, px + 12, py + 4);
});
// Update stats
const depths = embeddings.map(e => e.depth);
document.getElementById('nodeCount').textContent = embeddings.length;
document.getElementById('depth').textContent = Math.max(...depths);
}
// Compute hyperbolic distances
async function computeHyperbolicDistances(embeddings) {
const start = performance.now();
// Create embedding matrix
const dim = embeddings[0].embedding.length;
const numNodes = embeddings.length;
const keys = new Float32Array(numNodes * dim);
embeddings.forEach((node, i) => {
keys.set(node.embedding, i * dim);
});
// Compute distances for each node
let totalDistance = 0;
for (const node of embeddings) {
const similarities = await attention.hyperbolicAttention(node.embedding, keys);
const distances = Array.from(similarities).map(s => 1 - s);
totalDistance += distances.reduce((a, b) => a + b) / distances.length;
}
const avgDistance = totalDistance / embeddings.length;
const duration = performance.now() - start;
document.getElementById('avgDistance').textContent = avgDistance.toFixed(4);
document.getElementById('computeTime').textContent = `${duration.toFixed(2)}ms`;
return avgDistance;
}
// Load hierarchy
async function loadHierarchy(type) {
const hierarchy = hierarchies[type];
currentHierarchy = hierarchyToEmbeddings(hierarchy);
drawVisualization(currentHierarchy);
}
// Find nearest neighbors
async function findNearestNeighbors() {
if (!currentHierarchy) {
alert('Please load a hierarchy first');
return;
}
// Pick a random node
const queryNode = currentHierarchy[Math.floor(Math.random() * currentHierarchy.length)];
// Create keys from all nodes
const dim = queryNode.embedding.length;
const keys = new Float32Array(currentHierarchy.length * dim);
currentHierarchy.forEach((node, i) => {
keys.set(node.embedding, i * dim);
});
// Compute similarities
const similarities = await attention.hyperbolicAttention(queryNode.embedding, keys);
// Find top 5
const results = currentHierarchy
.map((node, i) => ({ node, similarity: similarities[i] }))
.sort((a, b) => b.similarity - a.similarity)
.slice(0, 5);
alert(
`Nearest neighbors to "${queryNode.name}":\n\n` +
results.map((r, i) =>
`${i + 1}. ${r.node.name} (similarity: ${r.similarity.toFixed(4)})`
).join('\n')
);
}
// Event listeners
document.getElementById('loadTaxonomy').addEventListener('click', () => loadHierarchy('taxonomy'));
document.getElementById('loadOrgChart').addEventListener('click', () => loadHierarchy('orgChart'));
document.getElementById('loadFilesystem').addEventListener('click', () => loadHierarchy('filesystem'));
document.getElementById('computeDistances').addEventListener('click', () => {
if (currentHierarchy) {
computeHyperbolicDistances(currentHierarchy);
} else {
alert('Please load a hierarchy first');
}
});
document.getElementById('findNearest').addEventListener('click', findNearestNeighbors);
// Initialize
initialize().then(() => {
loadHierarchy('taxonomy');
});
</script>
</body>
</html>