Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Commit Β·
663cec5
1
Parent(s): d00ac2c
add verbosity analysis plot from Thibaud
Browse files
app/src/content/chapters/analyses.mdx
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
| 1 |
## Analyses
|
| 2 |
|
| 3 |
Our final experiment explores an even more counterintuitive finding.
|
|
@@ -64,3 +67,13 @@ SmolLM2's quality distribution was actually reasonable:
|
|
| 64 |
| Poor | {'<'}30 tokens | 8% |
|
| 65 |
|
| 66 |
For pretraining data, diversity beats consistency. Models that don't follow instructions perfectly can produce better training data than those that do.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import HtmlEmbed from "../../components/HtmlEmbed.astro";
|
| 2 |
+
import FigRef from "../../components/FigRef.astro";
|
| 3 |
+
|
| 4 |
## Analyses
|
| 5 |
|
| 6 |
Our final experiment explores an even more counterintuitive finding.
|
|
|
|
| 67 |
| Poor | {'<'}30 tokens | 8% |
|
| 68 |
|
| 69 |
For pretraining data, diversity beats consistency. Models that don't follow instructions perfectly can produce better training data than those that do.
|
| 70 |
+
|
| 71 |
+
### Verbosity Analysis
|
| 72 |
+
|
| 73 |
+
Different prompt formats produce wildly different output lengths. <FigRef target="verbosity" /> shows the output tokens per document across four prompt types, broken down by model family. Table and Math prompts tend to be concise, while FAQ and Tutorial prompts generate significantly more tokens per document. Notably, the spread within each prompt type varies across model families: some models are consistently verbose regardless of the prompt, while others adapt their output length to the task.
|
| 74 |
+
|
| 75 |
+
<HtmlEmbed
|
| 76 |
+
id="verbosity"
|
| 77 |
+
src="verbosity.html"
|
| 78 |
+
desc="Output tokens per document across prompt types and model families. Hover over dots to see detailed statistics for each experiment."
|
| 79 |
+
/>
|
app/src/content/embeds/verbosity.html
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div class="d3-synth-banner" style="width:100%;margin:10px 0;aspect-ratio:3/1;min-height:260px;"></div>
|
| 2 |
+
<script>
|
| 3 |
+
(() => {
|
| 4 |
+
const ensureD3 = (cb) => {
|
| 5 |
+
if (window.d3 && typeof window.d3.select === 'function') return cb();
|
| 6 |
+
let s = document.getElementById('d3-cdn-script');
|
| 7 |
+
if (!s) {
|
| 8 |
+
s = document.createElement('script');
|
| 9 |
+
s.id = 'd3-cdn-script';
|
| 10 |
+
s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js';
|
| 11 |
+
document.head.appendChild(s);
|
| 12 |
+
}
|
| 13 |
+
const onReady = () => { if (window.d3 && typeof window.d3.select === 'function') cb(); };
|
| 14 |
+
s.addEventListener('load', onReady, { once: true });
|
| 15 |
+
if (window.d3) onReady();
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
+
const bootstrap = () => {
|
| 19 |
+
const mount = document.currentScript ? document.currentScript.previousElementSibling : null;
|
| 20 |
+
const container = (mount && mount.querySelector && mount.querySelector('.d3-synth-banner')) ||
|
| 21 |
+
Array.from(document.querySelectorAll('.d3-synth-banner')).find(el => el.dataset.mounted !== 'true');
|
| 22 |
+
if (!container) return;
|
| 23 |
+
if (container.dataset) {
|
| 24 |
+
if (container.dataset.mounted === 'true') return;
|
| 25 |
+
container.dataset.mounted = 'true';
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 29 |
+
// DATA β 65 rephrasing experiments
|
| 30 |
+
// [cat, prompt, model, source, compTokensB, promptTokensB,
|
| 31 |
+
// numDocsM, dclmDiff, outDclm, inDclm, eduDiff, outEdu, inEdu]
|
| 32 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 33 |
+
const raw = [
|
| 34 |
+
['Format','Article','Gemma-3 1B','FW-Edu HQ',4.00,5.66,19.7,-0.018,0.080,0.098,-0.432,1.484,1.679],
|
| 35 |
+
['Format','Commentary','Qwen3 1.7B','FW-Edu HQ',1.64,5.94,20.1,0.630,0.847,0.217,-0.229,3.533,3.762],
|
| 36 |
+
['Format','Commentary','Gemma-3 1B','DCLM',1.46,5.52,29.5,0.202,0.344,0.142,0.359,1.943,1.584],
|
| 37 |
+
['Format','Commentary','Gemma-3 1B','FW-Edu HQ',0.64,2.42,20.1,0.055,0.109,0.054,-0.517,0.793,0.921],
|
| 38 |
+
['Format','Discussion','Qwen3 1.7B','FW-Edu HQ',2.90,7.20,20.1,0.715,0.932,0.217,-0.122,3.640,3.762],
|
| 39 |
+
['Format','Discussion','Gemma-3 1B','FW-Edu HQ',3.96,6.25,20.1,0.036,0.161,0.125,-0.574,1.862,2.197],
|
| 40 |
+
['Format','FAQ','Gemma-3 12B','FW-Edu HQ',3.22,4.06,20.1,0.712,0.930,0.217,-0.295,3.467,3.762],
|
| 41 |
+
['Format','FAQ','Gemma-3 12B','FW-Edu LQ',1.91,1.90,32.9,0.799,0.828,0.029,0.357,1.263,0.906],
|
| 42 |
+
['Format','FAQ','Gemma-3 1B','Cosmopedia',1.33,1.31,31.1,0.329,0.906,0.578,-0.264,2.226,2.490],
|
| 43 |
+
['Format','FAQ','Gemma-3 1B','DCLM',1.52,2.08,29.5,0.709,0.851,0.142,0.346,1.930,1.585],
|
| 44 |
+
['Format','FAQ','Gemma-3 1B','FW-Edu HQ',17.67,22.21,20.1,0.711,0.929,0.218,-0.346,3.416,3.762],
|
| 45 |
+
['Format','FAQ','Gemma-3 1B','FW-Edu LQ',1.84,1.75,32.9,0.690,0.719,0.029,0.369,1.275,0.905],
|
| 46 |
+
['Format','FAQ','Falcon3 1B','FW-Edu HQ',3.41,4.84,20.1,0.691,0.909,0.217,-0.249,3.512,3.762],
|
| 47 |
+
['Format','FAQ','Granite3 1B','FW-Edu HQ',2.55,4.17,20.1,0.680,0.898,0.218,-0.166,3.596,3.762],
|
| 48 |
+
['Format','FAQ','Llama-3.2 1B','FW-Edu HQ',6.29,11.62,20.1,0.735,0.952,0.217,-0.375,3.387,3.762],
|
| 49 |
+
['Format','FAQ','Qwen3 1.7B','FW-Edu HQ',2.88,4.77,20.1,0.632,0.850,0.218,-0.219,3.543,3.762],
|
| 50 |
+
['Format','FAQ','SmolLM2 1.7B','FW-Edu HQ',0.89,2.00,20.1,0.648,0.867,0.219,-0.406,3.355,3.762],
|
| 51 |
+
['Format','Math','Gemma-3 12B','FW-Edu HQ',2.51,5.93,20.1,0.755,0.972,0.217,-0.370,3.392,3.762],
|
| 52 |
+
['Format','Math','Gemma-3 1B','FW-Edu HQ',2.46,5.08,20.1,0.620,0.838,0.218,-0.174,3.587,3.762],
|
| 53 |
+
['Format','Math','Gemma-3 270M','FW-Edu HQ',1.21,1.33,20.1,0.600,0.818,0.218,-0.406,3.357,3.762],
|
| 54 |
+
['Format','Math','Gemma-3 27B','FW-Edu HQ',1.05,2.62,20.1,0.754,0.971,0.217,-0.429,3.333,3.762],
|
| 55 |
+
['Format','Math','Gemma-3 4B','FW-Edu HQ',0.68,1.75,20.1,0.743,0.960,0.217,-0.335,3.427,3.762],
|
| 56 |
+
['Format','Math','Falcon3 1B','FW-Edu HQ',1.42,2.17,20.1,0.704,0.921,0.217,-0.156,3.606,3.762],
|
| 57 |
+
['Format','Math','Granite3 1B','FW-Edu HQ',1.57,3.35,20.1,0.698,0.916,0.218,-0.324,3.438,3.762],
|
| 58 |
+
['Format','Math','Llama-3.2 1B','FW-Edu HQ',0.86,2.17,20.1,0.737,0.955,0.218,-0.384,3.378,3.762],
|
| 59 |
+
['Format','Math','Qwen3 1.7B','FW-Edu HQ',0.56,2.00,20.1,0.742,0.959,0.218,-0.310,3.452,3.762],
|
| 60 |
+
['Format','Math','SmolLM2 1.7B','FW-Edu HQ',0.64,2.28,20.1,0.657,0.875,0.218,-0.669,3.093,3.762],
|
| 61 |
+
['Format','Table','Gemma-3 12B','FW-Edu HQ',0.76,2.49,20.1,0.719,0.937,0.217,-0.774,2.988,3.762],
|
| 62 |
+
['Format','Table','Gemma-3 1B','FW-Edu HQ',1.68,5.62,20.1,0.715,0.932,0.217,-0.776,2.985,3.762],
|
| 63 |
+
['Format','Table','Falcon3 1B','FW-Edu HQ',0.65,2.00,20.1,0.722,0.940,0.218,-0.748,3.015,3.763],
|
| 64 |
+
['Format','Table','Granite3 1B','FW-Edu HQ',1.44,3.90,20.1,0.726,0.944,0.218,-0.597,3.165,3.762],
|
| 65 |
+
['Format','Table','Llama-3.2 1B','FW-Edu HQ',0.88,2.73,20.1,0.772,0.989,0.217,-0.815,2.947,3.762],
|
| 66 |
+
['Format','Table','Qwen3 1.7B','FW-Edu HQ',0.80,2.69,20.1,0.751,0.969,0.218,-0.638,3.123,3.762],
|
| 67 |
+
['Format','Table','SmolLM2 1.7B','FW-Edu HQ',0.41,1.52,20.1,0.668,0.885,0.218,-0.943,2.818,3.762],
|
| 68 |
+
['Format','Tutorial','Gemma-3 12B','FW-Edu HQ',5.33,6.57,20.1,0.398,0.553,0.155,-0.077,2.612,2.667],
|
| 69 |
+
['Format','Tutorial','Gemma-3 12B','FW-Edu LQ',2.35,2.57,32.9,0.189,0.210,0.020,0.379,0.869,0.613],
|
| 70 |
+
['Format','Tutorial','Gemma-3 1B','Cosmopedia',1.02,1.47,31.1,-0.049,0.529,0.578,-0.080,2.415,2.495],
|
| 71 |
+
['Format','Tutorial','Gemma-3 1B','DCLM',1.67,3.30,29.5,0.371,0.514,0.142,0.528,2.113,1.585],
|
| 72 |
+
['Format','Tutorial','Gemma-3 1B','FW-Edu HQ',1.51,2.70,19.4,0.074,0.112,0.038,-0.077,0.632,0.646],
|
| 73 |
+
['Format','Tutorial','Gemma-3 1B','FW-Edu LQ',1.90,2.51,32.9,0.172,0.193,0.021,0.413,0.917,0.630],
|
| 74 |
+
['Format','Tutorial','Gemma-3 270M','FW-Edu HQ',1.64,1.71,20.1,0.514,0.731,0.217,-0.337,3.425,3.762],
|
| 75 |
+
['Format','Tutorial','Gemma-3 27B','FW-Edu HQ',2.55,3.29,20.1,0.485,0.702,0.218,-0.057,3.705,3.762],
|
| 76 |
+
['Format','Tutorial','Gemma-3 4B','FW-Edu HQ',1.50,2.02,20.1,0.404,0.621,0.217,-0.049,3.713,3.762],
|
| 77 |
+
['Format','Tutorial','Falcon3 1B','FW-Edu HQ',4.97,7.58,20.1,0.447,0.665,0.218,-0.145,3.617,3.762],
|
| 78 |
+
['Format','Tutorial','Granite3 1B','FW-Edu HQ',3.51,6.42,20.1,0.449,0.666,0.218,-0.072,3.690,3.762],
|
| 79 |
+
['Format','Tutorial','Llama-3.2 1B','FW-Edu HQ',2.36,4.88,20.1,0.557,0.774,0.217,-0.031,3.731,3.762],
|
| 80 |
+
['Format','Tutorial','Qwen1.5 1.8B','FW-Edu HQ',2.22,2.40,20.1,0.647,0.864,0.218,0.072,3.834,3.762],
|
| 81 |
+
['Format','Tutorial','Qwen2 1.5B','FW-Edu HQ',3.09,8.00,20.1,0.527,0.746,0.218,-0.010,3.751,3.762],
|
| 82 |
+
['Format','Tutorial','Qwen2.5 1.5B','FW-Edu HQ',2.27,4.87,20.1,0.576,0.794,0.218,-0.083,3.678,3.762],
|
| 83 |
+
['Format','Tutorial','Qwen3 1.7B','FW-Edu HQ',3.56,5.69,20.1,0.281,0.497,0.217,0.056,3.818,3.762],
|
| 84 |
+
['Format','Tutorial','SmolLM2 1.7B','FW-Edu HQ',1.52,3.38,20.1,0.439,0.658,0.219,-0.244,3.518,3.762],
|
| 85 |
+
['Nemotron','Distill','Gemma-3 1B','FW-Edu HQ',2.12,5.37,20.1,0.331,0.549,0.217,-0.132,3.629,3.762],
|
| 86 |
+
['Nemotron','Diverse QA','Gemma-3 1B','FW-Edu HQ',1.97,7.01,20.1,0.779,0.996,0.217,-0.208,3.554,3.762],
|
| 87 |
+
['Nemotron','Extract Knowledge','Gemma-3 1B','FW-Edu HQ',2.41,6.59,20.1,0.254,0.472,0.218,0.025,3.787,3.762],
|
| 88 |
+
['Nemotron','Knowledge List','Gemma-3 1B','FW-Edu HQ',2.90,9.15,20.1,0.589,0.806,0.217,-0.193,3.569,3.762],
|
| 89 |
+
['Nemotron','Wikipedia Style','Gemma-3 1B','FW-Edu HQ',3.43,8.37,20.1,0.297,0.515,0.217,-0.193,3.568,3.762],
|
| 90 |
+
['REWIRE','Guided Rewrite+','Gemma-3 12B','FW-Edu HQ',1.45,2.14,20.1,0.222,0.439,0.217,-0.102,3.660,3.762],
|
| 91 |
+
['REWIRE','Guided Rewrite+','Gemma-3 1B','FW-Edu HQ',1.63,3.42,20.1,0.097,0.315,0.217,-0.428,3.334,3.762],
|
| 92 |
+
['REWIRE','Guided Rewrite','Gemma-3 12B','FW-Edu HQ',1.78,1.72,20.1,0.607,0.824,0.217,-1.214,2.547,3.762],
|
| 93 |
+
['REWIRE','Guided Rewrite','Gemma-3 1B','FW-Edu HQ',8.69,10.30,20.1,0.278,0.495,0.217,-1.012,2.750,3.762],
|
| 94 |
+
['REWIRE','Guided Rewrite','Gemma-3 270M','FW-Edu HQ',0.78,1.71,20.1,0.755,0.972,0.217,-2.407,1.355,3.762],
|
| 95 |
+
['REWIRE','Guided Rewrite','Gemma-3 4B','FW-Edu HQ',0.91,1.10,20.1,0.436,0.654,0.218,-0.919,2.843,3.762],
|
| 96 |
+
['REWIRE','Guided Rewrite','Gemma-3 27B','FW-Edu HQ',8.43,8.64,19.95,0.546,0.763,0.217,-1.228,2.533,3.762],
|
| 97 |
+
['Format','Tutorial','SmolLM2 135M','FW-Edu HQ',4.51,6.99,20.1,0.423,0.641,0.218,-0.572,3.190,3.762],
|
| 98 |
+
['Format','Tutorial','SmolLM2 360M','FW-Edu HQ',3.14,6.52,20.1,0.441,0.659,0.218,-0.565,3.197,3.762]
|
| 99 |
+
];
|
| 100 |
+
|
| 101 |
+
const experiments = raw.map((r, i) => ({
|
| 102 |
+
idx: i, cat: r[0], prompt: r[1], model: r[2], source: r[3],
|
| 103 |
+
compTokens: r[4], promptTokens: r[5], numDocs: r[6],
|
| 104 |
+
dclmDiff: r[7], outDclm: r[8], inDclm: r[9],
|
| 105 |
+
eduDiff: r[10], outEdu: r[11], inEdu: r[12],
|
| 106 |
+
compPerDoc: r[4] * 1000 / r[6],
|
| 107 |
+
phase: Math.random() * Math.PI * 2
|
| 108 |
+
}));
|
| 109 |
+
|
| 110 |
+
// ββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½ββββββββββββββββ
|
| 111 |
+
// MODEL FAMILIES
|
| 112 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 113 |
+
const getFamily = (m) => {
|
| 114 |
+
if (m.includes('SmolLM')) return 'SmolLM2';
|
| 115 |
+
if (m.includes('Gemma')) return 'Gemma';
|
| 116 |
+
if (m.includes('Qwen')) return 'Qwen';
|
| 117 |
+
if (m.includes('Falcon')) return 'Falcon';
|
| 118 |
+
if (m.includes('Granite')) return 'Granite';
|
| 119 |
+
if (m.includes('Llama')) return 'Llama';
|
| 120 |
+
return 'Other';
|
| 121 |
+
};
|
| 122 |
+
const familyColors = {
|
| 123 |
+
'Gemma': '#4EA5B7',
|
| 124 |
+
'Qwen': '#8B7BE8',
|
| 125 |
+
'SmolLM2': '#E8C44A',
|
| 126 |
+
'Falcon': '#E889AB',
|
| 127 |
+
'Granite': '#5BC0A4',
|
| 128 |
+
'Llama': '#D09090',
|
| 129 |
+
};
|
| 130 |
+
const familyOrder = ['Gemma','Qwen','SmolLM2','Falcon','Granite','Llama'];
|
| 131 |
+
experiments.forEach(d => { d.family = getFamily(d.model); });
|
| 132 |
+
|
| 133 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 134 |
+
// FILTER: Only prompts tested across 6 model families
|
| 135 |
+
// (keep multi-source Gemma variants for Tutorial/FAQ)
|
| 136 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 137 |
+
const focusPrompts = ['Math', 'Table', 'FAQ', 'Tutorial'];
|
| 138 |
+
const filtered = experiments.filter(d => focusPrompts.includes(d.prompt));
|
| 139 |
+
|
| 140 |
+
// Sort rows by median compPerDoc (most verbose on top)
|
| 141 |
+
const promptStats = {};
|
| 142 |
+
focusPrompts.forEach(p => {
|
| 143 |
+
const vals = filtered.filter(d => d.prompt === p).map(d => d.compPerDoc).sort((a,b) => a - b);
|
| 144 |
+
const mid = Math.floor(vals.length / 2);
|
| 145 |
+
promptStats[p] = {
|
| 146 |
+
median: vals.length % 2 ? vals[mid] : (vals[mid-1] + vals[mid]) / 2,
|
| 147 |
+
count: vals.length
|
| 148 |
+
};
|
| 149 |
+
});
|
| 150 |
+
const sortedPrompts = [...focusPrompts].sort((a, b) => promptStats[b].median - promptStats[a].median);
|
| 151 |
+
|
| 152 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 153 |
+
// TOOLTIP
|
| 154 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 155 |
+
const fmtB = (v) => v >= 10 ? v.toFixed(0) + 'B' : v.toFixed(1) + 'B';
|
| 156 |
+
const fmtSign = (v, p) => (v >= 0 ? '+' : '') + v.toFixed(p || 3);
|
| 157 |
+
const dCol = (v) => v >= 0 ? '#5BC0A4' : '#E889AB';
|
| 158 |
+
const buildTip = (d) =>
|
| 159 |
+
`<div style="font-weight:800;font-size:13px;">${d.prompt}</div>` +
|
| 160 |
+
`<div style="font-size:11px;color:var(--muted-color);margin-top:-2px;margin-bottom:4px;">` +
|
| 161 |
+
`<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${familyColors[d.family]};margin-right:4px;vertical-align:middle;"></span>` +
|
| 162 |
+
`${d.model} \u00b7 ${d.source}</div>` +
|
| 163 |
+
`<div style="padding-top:5px;border-top:1px solid var(--border-color);display:grid;grid-template-columns:auto 1fr;gap:2px 8px;font-size:11.5px;">` +
|
| 164 |
+
`<span style="color:var(--muted-color);">Total output</span><span>${fmtB(d.compTokens)}</span>` +
|
| 165 |
+
`<span style="color:var(--muted-color);">Output/doc</span><span>${Math.round(d.compPerDoc)} tokens</span>` +
|
| 166 |
+
`<span style="color:var(--muted-color);">Docs</span><span>${d.numDocs.toFixed(1)}M</span>` +
|
| 167 |
+
`</div>` +
|
| 168 |
+
`<div style="margin-top:5px;padding-top:5px;border-top:1px solid var(--border-color);display:grid;grid-template-columns:auto 1fr;gap:2px 8px;font-size:11.5px;">` +
|
| 169 |
+
`<span style="color:var(--muted-color);">DCLM</span>` +
|
| 170 |
+
`<span>${d.inDclm.toFixed(3)} \u2192 ${d.outDclm.toFixed(3)} <b style="color:${dCol(d.dclmDiff)};">${fmtSign(d.dclmDiff)}</b></span>` +
|
| 171 |
+
`<span style="color:var(--muted-color);">Edu</span>` +
|
| 172 |
+
`<span>${d.inEdu.toFixed(2)} \u2192 ${d.outEdu.toFixed(2)} <b style="color:${dCol(d.eduDiff)};">${fmtSign(d.eduDiff, 2)}</b></span>` +
|
| 173 |
+
`</div>`;
|
| 174 |
+
|
| 175 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 176 |
+
// SVG
|
| 177 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 178 |
+
const svg = d3.select(container).append('svg')
|
| 179 |
+
.attr('width', '100%')
|
| 180 |
+
.style('display', 'block')
|
| 181 |
+
.style('cursor', 'crosshair');
|
| 182 |
+
|
| 183 |
+
let animFrame = null;
|
| 184 |
+
|
| 185 |
+
const render = () => {
|
| 186 |
+
const width = container.clientWidth || 800;
|
| 187 |
+
const height = Math.max(260, Math.round(width / 3));
|
| 188 |
+
svg.attr('width', width).attr('height', height);
|
| 189 |
+
|
| 190 |
+
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
| 191 |
+
const textColor = isDark ? 'rgba(255,255,255,0.72)' : 'rgba(0,0,0,0.62)';
|
| 192 |
+
const mutedText = isDark ? 'rgba(255,255,255,0.22)' : 'rgba(0,0,0,0.18)';
|
| 193 |
+
const subtleText = isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)';
|
| 194 |
+
const refLine = isDark ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.07)';
|
| 195 |
+
const bandEven = isDark ? 'rgba(255,255,255,0.022)' : 'rgba(0,0,0,0.018)';
|
| 196 |
+
const glowColor = isDark ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.25)';
|
| 197 |
+
const smolHighlight = isDark ? 'rgba(232,196,74,0.18)' : 'rgba(232,196,74,0.12)';
|
| 198 |
+
|
| 199 |
+
const fontSize = Math.max(8, Math.min(12, width / 80));
|
| 200 |
+
|
| 201 |
+
// βββ LAYOUT βββ
|
| 202 |
+
const labelW = Math.max(68, width * 0.085);
|
| 203 |
+
const pl = labelW + 12;
|
| 204 |
+
const pr = width * 0.965;
|
| 205 |
+
const pt = height * 0.20;
|
| 206 |
+
const pb = height * 0.86;
|
| 207 |
+
const plotH = pb - pt;
|
| 208 |
+
const rowH = plotH / sortedPrompts.length;
|
| 209 |
+
|
| 210 |
+
const rowCenter = {};
|
| 211 |
+
sortedPrompts.forEach((p, i) => { rowCenter[p] = pt + (i + 0.5) * rowH; });
|
| 212 |
+
|
| 213 |
+
// βββ X SCALE: completion tokens per document (log) βββ
|
| 214 |
+
const xScale = d3.scaleLog().base(10)
|
| 215 |
+
.domain([15, 1000])
|
| 216 |
+
.range([pl, pr]);
|
| 217 |
+
|
| 218 |
+
const rBase = Math.max(3.5, Math.min(8, width * 0.007));
|
| 219 |
+
|
| 220 |
+
// βββ FORCE SIMULATION βββ
|
| 221 |
+
filtered.forEach(d => {
|
| 222 |
+
d.tx = xScale(Math.max(15, d.compPerDoc));
|
| 223 |
+
d.ty = rowCenter[d.prompt];
|
| 224 |
+
d.x = d.tx + (Math.random() - 0.5) * 6;
|
| 225 |
+
d.y = d.ty + (Math.random() - 0.5) * rowH * 0.3;
|
| 226 |
+
});
|
| 227 |
+
|
| 228 |
+
const sim = d3.forceSimulation(filtered)
|
| 229 |
+
.force('x', d3.forceX(d => d.tx).strength(0.72))
|
| 230 |
+
.force('y', d3.forceY(d => d.ty).strength(0.55))
|
| 231 |
+
.force('collide', d3.forceCollide(rBase + 1.5).iterations(5))
|
| 232 |
+
.stop();
|
| 233 |
+
for (let i = 0; i < 250; i++) sim.tick();
|
| 234 |
+
|
| 235 |
+
filtered.forEach(d => {
|
| 236 |
+
d.x = Math.max(pl + rBase, Math.min(pr - rBase, d.x));
|
| 237 |
+
const yMin = rowCenter[d.prompt] - rowH * 0.46;
|
| 238 |
+
const yMax = rowCenter[d.prompt] + rowH * 0.46;
|
| 239 |
+
d.y = Math.max(yMin, Math.min(yMax, d.y));
|
| 240 |
+
});
|
| 241 |
+
|
| 242 |
+
// βββ BG STARS βββ
|
| 243 |
+
const bgCol = (() => {
|
| 244 |
+
const i01 = d3.interpolateRgb(d3.rgb(78,165,183), d3.rgb(206,192,250));
|
| 245 |
+
const i12 = d3.interpolateRgb(d3.rgb(206,192,250), d3.rgb(232,137,171));
|
| 246 |
+
return (v) => { const t = Math.max(0, Math.min(1, v)); return t <= 0.5 ? i01(t / 0.5) : i12((t - 0.5) / 0.5); };
|
| 247 |
+
})();
|
| 248 |
+
const gBg = svg.selectAll('g.bg').data([0]).join('g').attr('class', 'bg');
|
| 249 |
+
if (!gBg.selectAll('circle').size()) {
|
| 250 |
+
const stars = d3.range(50).map(() => ({
|
| 251 |
+
x: Math.random() * width, y: Math.random() * height,
|
| 252 |
+
z: Math.random(), r: 0.3 + Math.random() * 0.6
|
| 253 |
+
}));
|
| 254 |
+
gBg.selectAll('circle').data(stars).join('circle')
|
| 255 |
+
.attr('cx', d => d.x).attr('cy', d => d.y).attr('r', d => d.r)
|
| 256 |
+
.attr('fill', d => bgCol(d.z)).attr('fill-opacity', d => 0.03 + d.z * 0.04);
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
// βββ ROW BANDS & LABELS βββ
|
| 260 |
+
const gRows = svg.selectAll('g.rows').data([0]).join('g').attr('class', 'rows');
|
| 261 |
+
gRows.selectAll('*').remove();
|
| 262 |
+
|
| 263 |
+
sortedPrompts.forEach((p, i) => {
|
| 264 |
+
const y = rowCenter[p];
|
| 265 |
+
|
| 266 |
+
if (i % 2 === 0) {
|
| 267 |
+
gRows.append('rect')
|
| 268 |
+
.attr('x', pl - 6).attr('y', y - rowH / 2)
|
| 269 |
+
.attr('width', pr - pl + 12).attr('height', rowH)
|
| 270 |
+
.attr('fill', bandEven).attr('rx', 4);
|
| 271 |
+
}
|
| 272 |
+
if (i > 0) {
|
| 273 |
+
gRows.append('line')
|
| 274 |
+
.attr('x1', pl - 2).attr('x2', pr + 2)
|
| 275 |
+
.attr('y1', y - rowH / 2).attr('y2', y - rowH / 2)
|
| 276 |
+
.attr('stroke', refLine).attr('stroke-width', 0.5);
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
// SmolLM2 highlight zone β show where SmolLM2 dots land
|
| 280 |
+
const smolExp = filtered.filter(d => d.prompt === p && d.family === 'SmolLM2');
|
| 281 |
+
if (smolExp.length) {
|
| 282 |
+
const smolX = smolExp[0].x;
|
| 283 |
+
gRows.append('rect')
|
| 284 |
+
.attr('x', smolX - rBase * 2.5).attr('y', y - rowH * 0.42)
|
| 285 |
+
.attr('width', rBase * 5).attr('height', rowH * 0.84)
|
| 286 |
+
.attr('fill', smolHighlight).attr('rx', rBase * 2);
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
// Label
|
| 290 |
+
gRows.append('text')
|
| 291 |
+
.attr('x', pl - 10).attr('y', y)
|
| 292 |
+
.attr('text-anchor', 'end')
|
| 293 |
+
.attr('dominant-baseline', 'central')
|
| 294 |
+
.attr('fill', textColor)
|
| 295 |
+
.attr('font-size', fontSize + 'px')
|
| 296 |
+
.attr('font-weight', '700')
|
| 297 |
+
.attr('font-family', 'system-ui, -apple-system, sans-serif')
|
| 298 |
+
.attr('letter-spacing', '-0.2px')
|
| 299 |
+
.text(p);
|
| 300 |
+
});
|
| 301 |
+
|
| 302 |
+
// βββ X AXIS TICKS βββ
|
| 303 |
+
const gRef = svg.selectAll('g.ref').data([0]).join('g').attr('class', 'ref');
|
| 304 |
+
gRef.selectAll('*').remove();
|
| 305 |
+
|
| 306 |
+
[20, 50, 100, 200, 500].forEach(v => {
|
| 307 |
+
const x = xScale(v);
|
| 308 |
+
if (x > pl && x < pr) {
|
| 309 |
+
gRef.append('line')
|
| 310 |
+
.attr('x1', x).attr('x2', x)
|
| 311 |
+
.attr('y1', pb).attr('y2', pb + 3)
|
| 312 |
+
.attr('stroke', mutedText).attr('stroke-width', 0.7);
|
| 313 |
+
gRef.append('text')
|
| 314 |
+
.attr('x', x).attr('y', pb + 5)
|
| 315 |
+
.attr('text-anchor', 'middle')
|
| 316 |
+
.attr('dominant-baseline', 'hanging')
|
| 317 |
+
.attr('fill', mutedText)
|
| 318 |
+
.attr('font-size', (fontSize * 0.65) + 'px')
|
| 319 |
+
.attr('font-family', 'system-ui, -apple-system, sans-serif')
|
| 320 |
+
.text(v);
|
| 321 |
+
// Subtle grid line
|
| 322 |
+
gRef.append('line')
|
| 323 |
+
.attr('x1', x).attr('x2', x)
|
| 324 |
+
.attr('y1', pt).attr('y2', pb)
|
| 325 |
+
.attr('stroke', subtleText).attr('stroke-width', 0.4)
|
| 326 |
+
.attr('stroke-dasharray', '2,6');
|
| 327 |
+
}
|
| 328 |
+
});
|
| 329 |
+
|
| 330 |
+
// Axis label
|
| 331 |
+
gRef.append('text')
|
| 332 |
+
.attr('x', pr).attr('y', pb + 16)
|
| 333 |
+
.attr('text-anchor', 'end')
|
| 334 |
+
.attr('fill', mutedText)
|
| 335 |
+
.attr('font-size', (fontSize * 0.72) + 'px')
|
| 336 |
+
.attr('font-weight', '600')
|
| 337 |
+
.attr('font-family', 'system-ui, -apple-system, sans-serif')
|
| 338 |
+
.text('Output tokens / document \u2192');
|
| 339 |
+
|
| 340 |
+
// "Less verbose" / "More verbose" labels
|
| 341 |
+
gRef.append('text')
|
| 342 |
+
.attr('x', pl + 6).attr('y', pt - 5)
|
| 343 |
+
.attr('text-anchor', 'start')
|
| 344 |
+
.attr('fill', subtleText)
|
| 345 |
+
.attr('font-size', (fontSize * 0.62) + 'px')
|
| 346 |
+
.attr('font-style', 'italic')
|
| 347 |
+
.attr('font-family', 'system-ui, -apple-system, sans-serif')
|
| 348 |
+
.text('\u2190 concise');
|
| 349 |
+
|
| 350 |
+
gRef.append('text')
|
| 351 |
+
.attr('x', pr - 6).attr('y', pt - 5)
|
| 352 |
+
.attr('text-anchor', 'end')
|
| 353 |
+
.attr('fill', subtleText)
|
| 354 |
+
.attr('font-size', (fontSize * 0.62) + 'px')
|
| 355 |
+
.attr('font-style', 'italic')
|
| 356 |
+
.attr('font-family', 'system-ui, -apple-system, sans-serif')
|
| 357 |
+
.text('verbose \u2192');
|
| 358 |
+
|
| 359 |
+
// βββ MODEL FAMILY LEGEND βββ
|
| 360 |
+
const gLeg = svg.selectAll('g.legend').data([0]).join('g').attr('class', 'legend');
|
| 361 |
+
gLeg.selectAll('*').remove();
|
| 362 |
+
const legDotR = Math.max(3, fontSize * 0.35);
|
| 363 |
+
const legGap = Math.max(68, width * 0.082);
|
| 364 |
+
const totalLegW = familyOrder.length * legGap;
|
| 365 |
+
const legStartX = Math.max(pl, (width - totalLegW) / 2);
|
| 366 |
+
const legY = height * 0.055;
|
| 367 |
+
|
| 368 |
+
familyOrder.forEach((fam, i) => {
|
| 369 |
+
const lx = legStartX + i * legGap;
|
| 370 |
+
gLeg.append('circle')
|
| 371 |
+
.attr('cx', lx).attr('cy', legY)
|
| 372 |
+
.attr('r', legDotR)
|
| 373 |
+
.attr('fill', familyColors[fam]).attr('fill-opacity', 0.85);
|
| 374 |
+
gLeg.append('text')
|
| 375 |
+
.attr('x', lx + legDotR + 4).attr('y', legY)
|
| 376 |
+
.attr('dominant-baseline', 'central')
|
| 377 |
+
.attr('fill', isDark ? 'rgba(255,255,255,0.48)' : 'rgba(0,0,0,0.42)')
|
| 378 |
+
.attr('font-size', (fontSize * 0.72) + 'px')
|
| 379 |
+
.attr('font-weight', fam === 'SmolLM2' ? '700' : '500')
|
| 380 |
+
.attr('font-family', 'system-ui, -apple-system, sans-serif')
|
| 381 |
+
.text(fam);
|
| 382 |
+
});
|
| 383 |
+
|
| 384 |
+
// βββ TOOLTIP βββ
|
| 385 |
+
container.style.position = container.style.position || 'relative';
|
| 386 |
+
let tip = container.querySelector('.d3-tooltip');
|
| 387 |
+
let tipInner;
|
| 388 |
+
if (!tip) {
|
| 389 |
+
tip = document.createElement('div');
|
| 390 |
+
tip.className = 'd3-tooltip';
|
| 391 |
+
Object.assign(tip.style, {
|
| 392 |
+
position: 'absolute', top: '0px', left: '0px',
|
| 393 |
+
transform: 'translate(-9999px, -9999px)',
|
| 394 |
+
pointerEvents: 'none',
|
| 395 |
+
padding: '10px 14px', borderRadius: '12px',
|
| 396 |
+
fontSize: '12px', lineHeight: '1.4',
|
| 397 |
+
border: '1px solid var(--border-color)',
|
| 398 |
+
background: 'var(--surface-bg)',
|
| 399 |
+
color: 'var(--text-color)',
|
| 400 |
+
boxShadow: '0 8px 32px rgba(0,0,0,.28), 0 2px 8px rgba(0,0,0,.12)',
|
| 401 |
+
opacity: '0', transition: 'opacity .12s ease',
|
| 402 |
+
backdropFilter: 'saturate(1.12) blur(8px)',
|
| 403 |
+
zIndex: '20', maxWidth: '320px'
|
| 404 |
+
});
|
| 405 |
+
tipInner = document.createElement('div');
|
| 406 |
+
tipInner.className = 'd3-tooltip__inner';
|
| 407 |
+
Object.assign(tipInner.style, {
|
| 408 |
+
textAlign: 'left', display: 'flex', flexDirection: 'column',
|
| 409 |
+
gap: '4px', minWidth: '220px'
|
| 410 |
+
});
|
| 411 |
+
tip.appendChild(tipInner);
|
| 412 |
+
container.appendChild(tip);
|
| 413 |
+
} else {
|
| 414 |
+
tipInner = tip.querySelector('.d3-tooltip__inner') || tip;
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
// βββ DOTS βββ
|
| 418 |
+
const gDots = svg.selectAll('g.dots').data([0]).join('g').attr('class', 'dots');
|
| 419 |
+
|
| 420 |
+
const handleEnter = function (ev, d) {
|
| 421 |
+
gDots.selectAll('circle').attr('fill-opacity', 0.12).attr('stroke-opacity', 0.05);
|
| 422 |
+
// Highlight same family across all prompts
|
| 423 |
+
gDots.selectAll('circle')
|
| 424 |
+
.filter(c => c.family === d.family)
|
| 425 |
+
.attr('fill-opacity', 0.60).attr('stroke-opacity', 0.4);
|
| 426 |
+
d3.select(this)
|
| 427 |
+
.raise()
|
| 428 |
+
.attr('fill-opacity', 1)
|
| 429 |
+
.attr('stroke-opacity', 1)
|
| 430 |
+
.style('filter', `drop-shadow(0 0 8px ${glowColor})`)
|
| 431 |
+
.transition().duration(90).ease(d3.easeCubicOut)
|
| 432 |
+
.attr('r', rBase * 1.5);
|
| 433 |
+
tipInner.innerHTML = buildTip(d);
|
| 434 |
+
tip.style.opacity = '1';
|
| 435 |
+
};
|
| 436 |
+
const handleMove = (ev) => {
|
| 437 |
+
const [mx, my] = d3.pointer(ev, container);
|
| 438 |
+
const bw = tip.offsetWidth || 260;
|
| 439 |
+
const bh = tip.offsetHeight || 180;
|
| 440 |
+
const ox = (mx + bw + 20 > width) ? -(bw + 12) : 12;
|
| 441 |
+
const oy = (my + bh + 20 > height) ? -(bh + 12) : 14;
|
| 442 |
+
tip.style.transform = `translate(${Math.round(mx + ox)}px, ${Math.round(my + oy)}px)`;
|
| 443 |
+
};
|
| 444 |
+
const handleLeave = function (ev, d) {
|
| 445 |
+
gDots.selectAll('circle')
|
| 446 |
+
.attr('fill-opacity', 0.82)
|
| 447 |
+
.attr('stroke-opacity', 0.30);
|
| 448 |
+
d3.select(this)
|
| 449 |
+
.style('filter', null)
|
| 450 |
+
.transition().duration(90).ease(d3.easeCubicOut)
|
| 451 |
+
.attr('r', rBase);
|
| 452 |
+
tip.style.opacity = '0';
|
| 453 |
+
tip.style.transform = 'translate(-9999px, -9999px)';
|
| 454 |
+
};
|
| 455 |
+
|
| 456 |
+
gDots.selectAll('circle').data(filtered, d => d.idx)
|
| 457 |
+
.join(
|
| 458 |
+
enter => enter.append('circle')
|
| 459 |
+
.attr('cx', d => d.x).attr('cy', d => d.y)
|
| 460 |
+
.attr('r', rBase)
|
| 461 |
+
.attr('fill', d => familyColors[d.family] || '#aaa')
|
| 462 |
+
.attr('fill-opacity', 0.82)
|
| 463 |
+
.attr('stroke', d => d.family === 'SmolLM2' ? '#fff' : (familyColors[d.family] || '#aaa'))
|
| 464 |
+
.attr('stroke-width', d => d.family === 'SmolLM2' ? 1.5 : 0.9)
|
| 465 |
+
.attr('stroke-opacity', d => d.family === 'SmolLM2' ? 0.5 : 0.30)
|
| 466 |
+
.on('mouseenter', handleEnter)
|
| 467 |
+
.on('mousemove', handleMove)
|
| 468 |
+
.on('mouseleave', handleLeave),
|
| 469 |
+
update => update
|
| 470 |
+
.attr('cx', d => d.x).attr('cy', d => d.y)
|
| 471 |
+
.attr('r', rBase)
|
| 472 |
+
.attr('fill', d => familyColors[d.family] || '#aaa')
|
| 473 |
+
.attr('fill-opacity', 0.82)
|
| 474 |
+
.attr('stroke', d => d.family === 'SmolLM2' ? '#fff' : (familyColors[d.family] || '#aaa'))
|
| 475 |
+
.attr('stroke-width', d => d.family === 'SmolLM2' ? 1.5 : 0.9)
|
| 476 |
+
.attr('stroke-opacity', d => d.family === 'SmolLM2' ? 0.5 : 0.30)
|
| 477 |
+
.on('mouseenter', handleEnter)
|
| 478 |
+
.on('mousemove', handleMove)
|
| 479 |
+
.on('mouseleave', handleLeave)
|
| 480 |
+
);
|
| 481 |
+
|
| 482 |
+
// βββ BREATHING βββ
|
| 483 |
+
if (animFrame) cancelAnimationFrame(animFrame);
|
| 484 |
+
const breathe = () => {
|
| 485 |
+
const now = Date.now() * 0.001;
|
| 486 |
+
gDots.selectAll('circle').each(function (d) {
|
| 487 |
+
d3.select(this).attr('r', rBase + Math.sin(now * 0.5 + d.phase) * rBase * 0.03);
|
| 488 |
+
});
|
| 489 |
+
animFrame = requestAnimationFrame(breathe);
|
| 490 |
+
};
|
| 491 |
+
breathe();
|
| 492 |
+
};
|
| 493 |
+
|
| 494 |
+
if (window.ResizeObserver) {
|
| 495 |
+
new ResizeObserver(() => render()).observe(container);
|
| 496 |
+
} else {
|
| 497 |
+
window.addEventListener('resize', render);
|
| 498 |
+
}
|
| 499 |
+
render();
|
| 500 |
+
};
|
| 501 |
+
|
| 502 |
+
if (document.readyState === 'loading') {
|
| 503 |
+
document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true });
|
| 504 |
+
} else { ensureD3(bootstrap); }
|
| 505 |
+
})();
|
| 506 |
+
</script>
|