File size: 7,419 Bytes
f0f3d44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<!DOCTYPE html>
<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 (δ &gt; κ)</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 (δ &lt; 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>