Spaces:
Running
Running
| <!-- Compression: log-scale dumbbell of ours vs RPNI state counts across 12 datasets --> | |
| <div class="compression-bars"></div> | |
| <style> | |
| .compression-bars { position: relative; width: 100%; min-height: 440px; } | |
| .compression-bars svg { display: block; width: 100%; } | |
| .compression-bars .tick text { | |
| fill: var(--text-color); font-size: 11px; opacity: 0.6; | |
| font-variant-numeric: tabular-nums; | |
| } | |
| .compression-bars .tick line { display: none; } | |
| .compression-bars .domain { stroke: var(--border-color); opacity: 0.3; } | |
| .compression-bars .y-axis .domain { display: none; } | |
| .compression-bars .axis-label { | |
| fill: var(--text-color); font-size: 12px; font-weight: 500; | |
| } | |
| .compression-bars .cmp-row { transition: opacity 0.15s ease; } | |
| .compression-bars .tooltip { | |
| position: absolute; top: 0; left: 0; pointer-events: none; padding: 10px 14px; border-radius: 8px; | |
| font-size: 12px; line-height: 1.6; border: 1px solid var(--border-color); | |
| background: var(--surface-bg); color: var(--text-color); | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04); | |
| backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); | |
| opacity: 0; transition: opacity 0.15s ease; | |
| z-index: 100; max-width: 240px; | |
| font-variant-numeric: tabular-nums; | |
| } | |
| </style> | |
| <script> | |
| (() => { | |
| const ensureD3 = (cb) => { | |
| if (window.d3 && typeof window.d3.select === 'function') return cb(); | |
| let s = document.getElementById('d3-cdn-script'); | |
| if (!s) { s = document.createElement('script'); s.id = 'd3-cdn-script'; s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js'; document.head.appendChild(s); } | |
| s.addEventListener('load', () => cb(), { once: true }); | |
| }; | |
| const bootstrap = () => { | |
| const container = document.querySelector('.compression-bars:not([data-mounted])'); | |
| if (!container) return; | |
| container.dataset.mounted = 'true'; | |
| const d3 = window.d3; | |
| const data = [ | |
| { name: 'WebArena', ours: 25, rpni: 382, compression: 15, fitness: 1.000 }, | |
| { name: 'ATBench', ours: 15, rpni: 899, compression: 60, fitness: 1.000 }, | |
| { name: 'Mind2Web', ours: 8, rpni: 476, compression: 60, fitness: 1.000 }, | |
| { name: 'Who & When', ours: 9, rpni: 971, compression: 108, fitness: 1.000 }, | |
| { name: 'tau2-bench air', ours: 18, rpni: 6506, compression: 361, fitness: 1.000 }, | |
| { name: 'tau2-bench ret', ours: 19, rpni: 14249, compression: 750, fitness: 1.000 }, | |
| { name: 'SWE-smith', ours: 10, rpni: 11631, compression: 1163, fitness: 1.000 }, | |
| { name: 'OSWorld', ours: 27, rpni: 38232, compression: 1416, fitness: 0.997 }, | |
| { name: 'tau2-bench tel', ours: 43, rpni: 63897, compression: 1486, fitness: 1.000 }, | |
| { name: 'SWE-agent', ours: 25, rpni: 59510, compression: 2380, fitness: 0.999 }, | |
| { name: 'AgentNet', ours: 25, rpni: 62495, compression: 2500, fitness: 1.000 }, | |
| { name: 'GUI-Odyssey', ours: 7, rpni: 21255, compression: 3036, fitness: 1.000 }, | |
| ].sort((a, b) => a.compression - b.compression); | |
| const C_OURS = '#3d5a80'; // strong slate -- ours | |
| const C_RPNI = '#8fa6c4'; // light slate -- RPNI | |
| const margin = { top: 36, right: 78, bottom: 44, left: 120 }; | |
| const tip = document.createElement('div'); | |
| tip.className = 'tooltip'; | |
| container.appendChild(tip); | |
| let firstRender = true; | |
| function render() { | |
| container.querySelectorAll('svg').forEach(s => s.remove()); | |
| const rect = container.getBoundingClientRect(); | |
| const W = Math.max(300, Math.round(rect.width)); | |
| const rowH = 30; | |
| const H = margin.top + margin.bottom + data.length * rowH; | |
| const w = Math.max(0, W - margin.left - margin.right); | |
| const h = Math.max(0, H - margin.top - margin.bottom); | |
| const svg = d3.select(container).insert('svg', '.tooltip') | |
| .attr('width', W).attr('height', H); | |
| const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`); | |
| const x = d3.scaleLog().domain([5, 90000]).range([0, Math.max(0, w)]).clamp(true); | |
| const y = d3.scaleBand().domain(data.map(d => d.name)).range([0, Math.max(0, h)]).padding(0.32); | |
| // decade grid lines | |
| const ticks = [10, 100, 1000, 10000]; | |
| g.append('g').selectAll('line').data(ticks).enter().append('line') | |
| .attr('x1', d => x(d)).attr('x2', d => x(d)) | |
| .attr('y1', -6).attr('y2', Math.max(0, h)) | |
| .attr('stroke', 'var(--border-color)').attr('stroke-opacity', 0.25) | |
| .attr('stroke-dasharray', '2,4'); | |
| // x axis | |
| g.append('g').attr('transform', `translate(0,${Math.max(0, h)})`) | |
| .call(d3.axisBottom(x).tickValues(ticks).tickFormat(d => d >= 1000 ? (d / 1000) + 'k' : d + '')) | |
| .call(s => s.select('.domain').attr('opacity', 0.3)) | |
| .selectAll('.tick text').attr('opacity', 0.6); | |
| g.append('text').attr('class', 'axis-label') | |
| .attr('x', Math.max(0, w) / 2).attr('y', Math.max(0, h) + 36).attr('text-anchor', 'middle') | |
| .text('State count, log scale (gap = compression)'); | |
| // y labels | |
| g.append('g').attr('class', 'y-axis') | |
| .call(d3.axisLeft(y).tickSize(0).tickPadding(10)) | |
| .call(s => s.select('.domain').remove()) | |
| .selectAll('.tick text').attr('font-size', '11px').attr('opacity', 0.7); | |
| // legend | |
| const leg = g.append('g').attr('transform', 'translate(0, -22)'); | |
| leg.append('circle').attr('cx', 0).attr('cy', 0).attr('r', 5).attr('fill', C_OURS); | |
| leg.append('text').attr('x', 10).attr('y', 4).attr('fill', 'var(--text-color)').attr('font-size', '11px').attr('opacity', 0.75).text('Ours (FSM)'); | |
| leg.append('circle').attr('cx', 96).attr('cy', 0).attr('r', 5).attr('fill', C_RPNI); | |
| leg.append('text').attr('x', 106).attr('y', 4).attr('fill', 'var(--text-color)').attr('font-size', '11px').attr('opacity', 0.75).text('RPNI'); | |
| const rows = g.selectAll('.cmp-row').data(data).enter().append('g') | |
| .attr('class', 'cmp-row') | |
| .attr('transform', d => `translate(0, ${y(d.name) + y.bandwidth() / 2})`) | |
| .style('cursor', 'pointer'); | |
| const lines = rows.append('line') | |
| .attr('x1', d => x(d.ours)).attr('y1', 0).attr('y2', 0) | |
| .attr('stroke', C_RPNI).attr('stroke-width', 2).attr('stroke-opacity', 0.5) | |
| .attr('stroke-linecap', 'round'); | |
| rows.append('circle').attr('class', 'dot-rpni') | |
| .attr('cx', d => x(d.rpni)).attr('cy', 0).attr('r', 5).attr('fill', C_RPNI); | |
| rows.append('circle').attr('class', 'dot-ours') | |
| .attr('cx', d => x(d.ours)).attr('cy', 0).attr('r', 5).attr('fill', C_OURS); | |
| const ratioText = rows.append('text') | |
| .attr('x', d => x(d.rpni) + 9).attr('y', 4) | |
| .attr('fill', 'var(--text-color)').attr('font-size', '10px').attr('font-weight', 600) | |
| .attr('font-variant-numeric', 'tabular-nums').attr('opacity', 0.85) | |
| .text(d => d.compression.toLocaleString() + '×'); | |
| if (firstRender) { | |
| lines.attr('x2', d => x(d.ours)) | |
| .transition().duration(650).delay((d, i) => i * 45).ease(d3.easeCubicOut) | |
| .attr('x2', d => x(d.rpni)); | |
| rows.select('.dot-rpni').attr('cx', d => x(d.ours)) | |
| .transition().duration(650).delay((d, i) => i * 45).ease(d3.easeCubicOut) | |
| .attr('cx', d => x(d.rpni)); | |
| ratioText.attr('opacity', 0) | |
| .transition().duration(300).delay((d, i) => i * 45 + 550).attr('opacity', 0.85); | |
| } else { | |
| lines.attr('x2', d => x(d.rpni)); | |
| } | |
| rows | |
| .on('mouseenter', function(ev, d) { | |
| d3.select(this).selectAll('circle').attr('r', 6.5); | |
| tip.innerHTML = [ | |
| '<strong>' + d.name + '</strong>', | |
| 'Ours: ' + d.ours + ' states', | |
| 'RPNI: ' + d.rpni.toLocaleString() + ' states', | |
| 'Compression: <strong>' + d.compression.toLocaleString() + '×</strong>', | |
| 'Fitness: ' + d.fitness.toFixed(3) | |
| ].join('<br/>'); | |
| tip.style.opacity = '1'; | |
| }) | |
| .on('mousemove', function(ev) { | |
| const [mx, my] = d3.pointer(ev, container); | |
| const tipW = tip.offsetWidth || 180, tipH = tip.offsetHeight || 80; | |
| const cRect = container.getBoundingClientRect(); | |
| let tx = mx + 14, ty = my - 14; | |
| if (tx + tipW > cRect.width) tx = mx - tipW - 14; | |
| if (ty + tipH > cRect.height) ty = my - tipH - 14; | |
| tip.style.transform = `translate(${tx}px, ${ty}px)`; | |
| }) | |
| .on('mouseleave', function() { | |
| d3.select(this).selectAll('circle').attr('r', 5); | |
| tip.style.opacity = '0'; | |
| }); | |
| firstRender = false; | |
| } | |
| render(); | |
| if (window.ResizeObserver) new ResizeObserver(() => render()).observe(container); | |
| }; | |
| if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true }); | |
| else ensureD3(bootstrap); | |
| })(); | |
| </script> | |