Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <style> | |
| :root { --bg: transparent; --text: #e8eaf0; --subtext: #8b8fa8; --grid: rgba(255,255,255,0.08); } | |
| * { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { background: var(--bg); font-family: system-ui, sans-serif; color: var(--text); } | |
| .controls { | |
| display: flex; align-items: center; gap: 14px; justify-content: center; | |
| margin: 0 0 4px; font-size: 14px; flex-wrap: wrap; | |
| } | |
| .controls label { color: var(--subtext); font-weight: 600; } | |
| .controls input[type=range] { | |
| width: 140px; accent-color: #6366f1; cursor: pointer; | |
| } | |
| .kval { color: #e8eaf0; font-weight: 700; min-width: 48px; } | |
| .axis text { fill: #8b8fa8; font-size: 12px; } | |
| .axis path, .axis line { stroke: rgba(255,255,255,0.15); } | |
| .grid line { stroke: var(--grid); } | |
| .grid path { stroke: none; } | |
| .legend-row { | |
| display: flex; gap: 16px; justify-content: center; font-size: 13px; | |
| color: var(--subtext); margin-top: 6px; flex-wrap: wrap; | |
| } | |
| .legend-row span { display: flex; align-items: center; gap: 5px; } | |
| .legend-swatch { width: 12px; height: 4px; border-radius: 2px; flex-shrink: 0; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="controls"> | |
| <label>κ threshold:</label> | |
| <input type="range" id="kappa-slider" min="0" max="0.05" step="0.001" value="0.015"/> | |
| <span class="kval" id="kappa-val">0.015</span> | |
| </div> | |
| <svg id="rabc-svg"></svg> | |
| <div class="legend-row"> | |
| <span><span class="legend-swatch" style="background:#22d3ee"></span>SARM progress</span> | |
| <span><span class="legend-swatch" style="background:#10b981"></span>Full weight (δ > κ)</span> | |
| <span><span class="legend-swatch" style="background:#eab308"></span>Soft weight (0 ≤ δ ≤ κ)</span> | |
| <span><span class="legend-swatch" style="background:#ef4444"></span>Zero weight (δ < 0)</span> | |
| </div> | |
| <script> | |
| function _initRABC() { | |
| // Simulated SARM progress for a realistic folding episode | |
| const N = 120; | |
| const progressRaw = []; | |
| const seed = [ | |
| 0,0.01,0.02,0.03,0.04,0.06,0.08,0.10,0.12,0.14, | |
| 0.16,0.18,0.20,0.22,0.24,0.26,0.28,0.28,0.27,0.26, | |
| 0.26,0.27,0.28,0.30,0.32,0.34,0.36,0.38,0.40,0.42, | |
| 0.44,0.45,0.45,0.45,0.44,0.44,0.45,0.46,0.48,0.50, | |
| 0.52,0.54,0.56,0.58,0.60,0.62,0.64,0.66,0.68,0.70, | |
| 0.72,0.73,0.74,0.74,0.73,0.72,0.71,0.70,0.70,0.71, | |
| 0.72,0.74,0.76,0.78,0.80,0.81,0.82,0.83,0.84,0.85, | |
| 0.85,0.85,0.86,0.86,0.87,0.88,0.89,0.90,0.91,0.92, | |
| 0.93,0.93,0.93,0.93,0.94,0.94,0.95,0.95,0.96,0.96, | |
| 0.96,0.97,0.97,0.97,0.98,0.98,0.98,0.98,0.99,0.99, | |
| 0.99,0.99,0.99,1.0,1.0,1.0,1.0,1.0,1.0,1.0, | |
| 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 | |
| ]; | |
| for (let i = 0; i < N; i++) progressRaw.push(seed[i]); | |
| const CHUNK = 5; | |
| function computeDeltas(progress) { | |
| const deltas = []; | |
| for (let i = 0; i < progress.length; i++) { | |
| const j = Math.min(i + CHUNK, progress.length - 1); | |
| deltas.push(progress[j] - progress[i]); | |
| } | |
| return deltas; | |
| } | |
| function computeWeights(deltas, kappa) { | |
| const posDeltas = deltas.filter(d => d >= 0); | |
| let mu = posDeltas.length ? posDeltas.reduce((a,b)=>a+b,0)/posDeltas.length : 0; | |
| mu = Math.max(mu, 0); | |
| const variance = posDeltas.length > 1 | |
| ? posDeltas.reduce((s,d)=>s+(d-mu)*(d-mu),0)/(posDeltas.length-1) : 0.0001; | |
| const sigma = Math.sqrt(variance); | |
| return deltas.map(d => { | |
| if (d > kappa) return 1.0; | |
| if (d < 0) return 0.0; | |
| const lo = mu - 2*sigma, hi = mu + 2*sigma; | |
| const range = hi - lo; | |
| if (range < 0.0001) return d >= 0 ? 1 : 0; | |
| return Math.max(0, Math.min(1, (d - lo) / range)); | |
| }); | |
| } | |
| const container = document.getElementById("rabc-svg").parentElement; | |
| const svg = d3.select("#rabc-svg"); | |
| function render() { | |
| svg.selectAll("*").remove(); | |
| const kappa = parseFloat(document.getElementById("kappa-slider").value); | |
| document.getElementById("kappa-val").textContent = kappa.toFixed(3); | |
| const deltas = computeDeltas(progressRaw); | |
| const weights = computeWeights(deltas, kappa); | |
| const W = container.clientWidth || 700; | |
| const margin = { top: 12, right: 16, bottom: 56, left: 46 }; | |
| const totalH = Math.max(260, Math.min(360, W * 0.42)); | |
| const progressH = totalH * 0.55; | |
| const gap = 24; | |
| const weightH = totalH - progressH - gap; | |
| const H = totalH + margin.top + margin.bottom; | |
| const w = W - margin.left - margin.right; | |
| svg.attr("width", W).attr("height", H); | |
| const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`); | |
| const x = d3.scaleLinear().domain([0, N-1]).range([0, w]); | |
| const yP = d3.scaleLinear().domain([0, 1]).range([progressH, 0]); | |
| const yW = d3.scaleLinear().domain([0, 1]).range([weightH, 0]); | |
| const wG = g.append("g").attr("transform", `translate(0,${progressH + gap})`); | |
| // Progress grid + axis | |
| g.append("g").attr("class","grid") | |
| .call(d3.axisLeft(yP).ticks(4).tickSize(-w).tickFormat("")); | |
| g.append("g").attr("class","axis") | |
| .call(d3.axisLeft(yP).ticks(4).tickFormat(d3.format(".1f"))); | |
| g.append("text").attr("x",-progressH/2).attr("y",-36).attr("transform","rotate(-90)") | |
| .attr("text-anchor","middle").attr("fill","#8b8fa8").attr("font-size",12).text("SARM progress"); | |
| // Weight grid + axis | |
| wG.append("g").attr("class","grid") | |
| .call(d3.axisLeft(yW).ticks(3).tickSize(-w).tickFormat("")); | |
| wG.append("g").attr("class","axis") | |
| .call(d3.axisLeft(yW).ticks(3).tickFormat(d3.format(".1f"))); | |
| wG.append("text").attr("x",-weightH/2).attr("y",-36).attr("transform","rotate(-90)") | |
| .attr("text-anchor","middle").attr("fill","#8b8fa8").attr("font-size",12).text("Training weight"); | |
| // Shared x axis | |
| wG.append("g").attr("class","axis").attr("transform",`translate(0,${weightH})`) | |
| .call(d3.axisBottom(x).ticks(6).tickFormat(d => d + "")); | |
| wG.append("text").attr("x",w/2).attr("y",weightH+30).attr("text-anchor","middle") | |
| .attr("fill","#8b8fa8").attr("font-size",12).text("Timestep"); | |
| // Color weight bars | |
| const barW = Math.max(1, w / N - 0.5); | |
| weights.forEach((wt, i) => { | |
| const delta = deltas[i]; | |
| let col; | |
| if (delta < 0) col = "#ef4444"; | |
| else if (delta > kappa) col = "#10b981"; | |
| else col = "#eab308"; | |
| wG.append("rect") | |
| .attr("x", x(i) - barW/2).attr("y", yW(wt)) | |
| .attr("width", barW).attr("height", Math.max(0, weightH - yW(wt))) | |
| .attr("fill", col).attr("opacity", 0.8); | |
| }); | |
| // Progress curve background shading | |
| for (let i = 0; i < N - 1; i++) { | |
| const delta = deltas[i]; | |
| let col; | |
| if (delta < 0) col = "#ef4444"; | |
| else if (delta > kappa) col = "#10b981"; | |
| else col = "#eab308"; | |
| g.append("rect") | |
| .attr("x", x(i)).attr("y", 0) | |
| .attr("width", Math.max(1, x(i+1) - x(i))).attr("height", progressH) | |
| .attr("fill", col).attr("opacity", 0.07); | |
| } | |
| // Progress line | |
| const line = d3.line().x((d,i) => x(i)).y(d => yP(d)).curve(d3.curveMonotoneX); | |
| g.append("path").datum(progressRaw) | |
| .attr("fill","none").attr("stroke","#22d3ee").attr("stroke-width",2.5).attr("opacity",0.9) | |
| .attr("d", line); | |
| } | |
| render(); | |
| document.getElementById("kappa-slider").addEventListener("input", render); | |
| window.addEventListener("resize", render); | |
| } | |
| if (typeof d3 !== "undefined") { _initRABC(); } | |
| else { var s = document.createElement("script"); s.src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"; s.onload=_initRABC; document.head.appendChild(s); } | |
| </script> | |
| </body> | |
| </html> | |