Spaces:
Running
Running
Upload index.html
Browse files- index.html +405 -18
index.html
CHANGED
|
@@ -1,19 +1,406 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>RoPE — Full Rotation Matrix</title>
|
| 7 |
+
<style>
|
| 8 |
+
:root {
|
| 9 |
+
--color-background-primary: #ffffff;
|
| 10 |
+
--color-background-secondary: #f5f4f0;
|
| 11 |
+
--color-background-tertiary: #eeecea;
|
| 12 |
+
--color-background-info: #e6f1fb;
|
| 13 |
+
--color-text-primary: #1a1a18;
|
| 14 |
+
--color-text-secondary: #73726c;
|
| 15 |
+
--color-text-info: #185fa5;
|
| 16 |
+
--color-border-tertiary: rgba(0,0,0,0.12);
|
| 17 |
+
--color-border-secondary: rgba(0,0,0,0.22);
|
| 18 |
+
--color-border-info: #185fa5;
|
| 19 |
+
--font-sans: "Anthropic Sans", system-ui, sans-serif;
|
| 20 |
+
}
|
| 21 |
+
@media (prefers-color-scheme: dark) {
|
| 22 |
+
:root {
|
| 23 |
+
--color-background-primary: #1e1e1c;
|
| 24 |
+
--color-background-secondary: #2a2a27;
|
| 25 |
+
--color-background-tertiary: #222220;
|
| 26 |
+
--color-background-info: #0c2a42;
|
| 27 |
+
--color-text-primary: #e8e6de;
|
| 28 |
+
--color-text-secondary: #9c9a92;
|
| 29 |
+
--color-text-info: #85b7eb;
|
| 30 |
+
--color-border-tertiary: rgba(255,255,255,0.12);
|
| 31 |
+
--color-border-secondary: rgba(255,255,255,0.22);
|
| 32 |
+
--color-border-info: #378add;
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
| 36 |
+
body { font-family: var(--font-sans); background: var(--color-background-primary); color: var(--color-text-primary); padding: 1.5rem; }
|
| 37 |
+
.pe-wrap { padding: 0.5rem 0 1rem; }
|
| 38 |
+
.pe-title { font-size: 15px; font-weight: 500; color: var(--color-text-primary); margin-bottom: 4px; }
|
| 39 |
+
.pe-sub { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 14px; line-height: 1.6; }
|
| 40 |
+
.pe-axis-label { font-size: 12px; color: var(--color-text-secondary); text-align: center; }
|
| 41 |
+
.pe-y-label { writing-mode: vertical-rl; transform: rotate(180deg); font-size: 12px; color: var(--color-text-secondary); display: flex; align-items: center; justify-content: center; min-width: 20px; }
|
| 42 |
+
.pe-row { display: flex; align-items: stretch; gap: 8px; }
|
| 43 |
+
.pe-legend { display: flex; align-items: center; gap: 8px; margin-top: 8px; font-size: 11px; color: var(--color-text-secondary); }
|
| 44 |
+
.annotation-box { background: var(--color-background-secondary); border: 0.5px solid var(--color-border-tertiary); border-radius: 8px; padding: 10px 14px; margin-top: 12px; font-size: 12px; color: var(--color-text-secondary); line-height: 1.6; }
|
| 45 |
+
.ann-row { display: flex; gap: 16px; }
|
| 46 |
+
.ann-col { flex: 1; }
|
| 47 |
+
.ann-head { font-weight: 500; font-size: 12px; color: var(--color-text-primary); margin-bottom: 3px; }
|
| 48 |
+
.controls-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; align-items: center; }
|
| 49 |
+
.pe-btn { background: var(--color-background-secondary); border: 0.5px solid var(--color-border-secondary); border-radius: 6px; padding: 4px 11px; font-size: 12px; cursor: pointer; color: var(--color-text-primary); }
|
| 50 |
+
.pe-btn.active { background: var(--color-background-info); color: var(--color-text-info); border-color: var(--color-border-info); }
|
| 51 |
+
.tooltip { position: absolute; background: var(--color-background-primary); border: 0.5px solid var(--color-border-secondary); border-radius: 6px; padding: 6px 10px; font-size: 11px; color: var(--color-text-primary); pointer-events: none; opacity: 0; transition: opacity 0.15s; white-space: nowrap; z-index: 10; line-height: 1.6; }
|
| 52 |
+
.badge { display: inline-block; font-size: 10px; padding: 2px 6px; border-radius: 4px; background: var(--color-background-info); color: var(--color-text-info); border: 0.5px solid var(--color-border-info); margin-left: 6px; }
|
| 53 |
+
.divider { border: none; border-top: 0.5px solid var(--color-border-tertiary); margin: 8px 0; }
|
| 54 |
+
.dual-wrap { display: flex; gap: 6px; flex: 1; }
|
| 55 |
+
.matrix-panel { flex: 1; position: relative; }
|
| 56 |
+
</style>
|
| 57 |
+
</head>
|
| 58 |
+
<body>
|
| 59 |
+
<div class="pe-wrap">
|
| 60 |
+
<div class="pe-title">RoPE — Full Rotation Matrix <span class="badge">Rotary Position Encoding</span></div>
|
| 61 |
+
<div class="pe-sub">
|
| 62 |
+
RoPE rotates each dimension pair (q<sub>2i</sub>, q<sub>2i+1</sub>) by angle <strong>pos · θ<sub>i</sub></strong> using a 2×2 rotation matrix — requiring both <strong style="color:#0F6E56">cos</strong> and <strong style="color:#BA7517">sin</strong>. Toggle between three views of the same underlying data.
|
| 63 |
+
</div>
|
| 64 |
+
|
| 65 |
+
<div class="controls-row">
|
| 66 |
+
<span style="font-size:12px;color:var(--color-text-secondary)">View:</span>
|
| 67 |
+
<button class="pe-btn active" id="btn-view-sidebyside" onclick="setView('sidebyside')">cos | sin side by side</button>
|
| 68 |
+
<button class="pe-btn" id="btn-view-interleaved" onclick="setView('interleaved')">interleaved (as in code)</button>
|
| 69 |
+
<button class="pe-btn" id="btn-view-angle" onclick="setView('angle')">rotation angle φ = pos·θ</button>
|
| 70 |
+
</div>
|
| 71 |
+
<div class="controls-row" style="margin-bottom:10px">
|
| 72 |
+
<span style="font-size:12px;color:var(--color-text-secondary)">Hover mode:</span>
|
| 73 |
+
<button class="pe-btn active" id="btn-cell" onclick="setMode('cell')">Cell value</button>
|
| 74 |
+
<button class="pe-btn" id="btn-col" onclick="setMode('col')">Column (dim pair)</button>
|
| 75 |
+
<button class="pe-btn" id="btn-row" onclick="setMode('row')">Row (position)</button>
|
| 76 |
+
</div>
|
| 77 |
+
|
| 78 |
+
<!-- Side-by-side view -->
|
| 79 |
+
<div id="view-sidebyside">
|
| 80 |
+
<div style="display:flex;gap:6px;margin-left:28px;margin-bottom:3px">
|
| 81 |
+
<div style="flex:1;text-align:center;font-size:11px;font-weight:500;color:#0F6E56;background:rgba(29,158,117,0.1);border-radius:4px;padding:2px 0">cos(pos · θ<sub>i</sub>)</div>
|
| 82 |
+
<div style="flex:1;text-align:center;font-size:11px;font-weight:500;color:#854F0B;background:rgba(186,117,23,0.1);border-radius:4px;padding:2px 0">sin(pos · θ<sub>i</sub>)</div>
|
| 83 |
+
</div>
|
| 84 |
+
<div class="pe-row">
|
| 85 |
+
<div class="pe-y-label">Sequence position <em>(pos ↓)</em></div>
|
| 86 |
+
<div class="dual-wrap">
|
| 87 |
+
<div class="matrix-panel">
|
| 88 |
+
<canvas id="pe-canvas-cos" style="width:100%;display:block;cursor:crosshair;"></canvas>
|
| 89 |
+
<div class="tooltip" id="tt-cos"></div>
|
| 90 |
+
</div>
|
| 91 |
+
<div style="width:1px;background:var(--color-border-tertiary);margin:0 1px;flex-shrink:0"></div>
|
| 92 |
+
<div class="matrix-panel">
|
| 93 |
+
<canvas id="pe-canvas-sin" style="width:100%;display:block;cursor:crosshair;"></canvas>
|
| 94 |
+
<div class="tooltip" id="tt-sin"></div>
|
| 95 |
+
</div>
|
| 96 |
+
</div>
|
| 97 |
+
</div>
|
| 98 |
+
<div style="margin-left:28px">
|
| 99 |
+
<div class="pe-axis-label">Dimension pair index <em>(i →)</em></div>
|
| 100 |
+
<div style="display:flex;gap:6px;margin-top:2px">
|
| 101 |
+
<div style="flex:1;display:flex;justify-content:space-between;font-size:10px;color:var(--color-text-secondary)"><span>0 (fast)</span><span>64</span><span>127 (slow)</span></div>
|
| 102 |
+
<div style="width:3px;flex-shrink:0"></div>
|
| 103 |
+
<div style="flex:1;display:flex;justify-content:space-between;font-size:10px;color:var(--color-text-secondary)"><span>0 (fast)</span><span>64</span><span>127 (slow)</span></div>
|
| 104 |
+
</div>
|
| 105 |
+
</div>
|
| 106 |
+
<div class="pe-legend" style="margin-left:28px">
|
| 107 |
+
<span>−1</span>
|
| 108 |
+
<canvas id="legend-bar-sbs" height="12" style="flex:1;border-radius:3px;"></canvas>
|
| 109 |
+
<span>+1</span>
|
| 110 |
+
</div>
|
| 111 |
+
</div>
|
| 112 |
+
|
| 113 |
+
<!-- Interleaved view -->
|
| 114 |
+
<div id="view-interleaved" style="display:none">
|
| 115 |
+
<div style="margin-left:28px;margin-bottom:3px;font-size:11px;color:var(--color-text-secondary)">
|
| 116 |
+
Columns alternate <strong style="color:#0F6E56">cos</strong>, <strong style="color:#854F0B">sin</strong>, <strong style="color:#0F6E56">cos</strong>, <strong style="color:#854F0B">sin</strong>… matching how <code>cos_pos</code> and <code>sin_pos</code> are broadcast in the code via <code>repeat(..., rep=2)</code>.
|
| 117 |
+
</div>
|
| 118 |
+
<div class="pe-row">
|
| 119 |
+
<div class="pe-y-label">Sequence position <em>(pos ↓)</em></div>
|
| 120 |
+
<div style="flex:1;position:relative;">
|
| 121 |
+
<canvas id="pe-canvas-interleaved" style="width:100%;display:block;cursor:crosshair;"></canvas>
|
| 122 |
+
<div class="tooltip" id="tt-il"></div>
|
| 123 |
+
</div>
|
| 124 |
+
</div>
|
| 125 |
+
<div style="margin-left:28px">
|
| 126 |
+
<div class="pe-axis-label">Embedding dimension d (pairs of columns = one rotation)</div>
|
| 127 |
+
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--color-text-secondary);margin-top:2px"><span>d=0 cos</span><span>d=128</span><span>d=255 sin</span></div>
|
| 128 |
+
</div>
|
| 129 |
+
<div class="pe-legend" style="margin-left:28px">
|
| 130 |
+
<span>−1</span>
|
| 131 |
+
<canvas id="legend-bar-il" height="12" style="flex:1;border-radius:3px;"></canvas>
|
| 132 |
+
<span>+1</span>
|
| 133 |
+
</div>
|
| 134 |
+
</div>
|
| 135 |
+
|
| 136 |
+
<!-- Angle view -->
|
| 137 |
+
<div id="view-angle" style="display:none">
|
| 138 |
+
<div style="margin-left:28px;margin-bottom:3px;font-size:11px;color:var(--color-text-secondary)">
|
| 139 |
+
The raw rotation angle φ = pos · θ<sub>i</sub> in radians. Both cos and sin are derived from this single scalar. Colour = angle magnitude (dark = 0, bright = max).
|
| 140 |
+
</div>
|
| 141 |
+
<div class="pe-row">
|
| 142 |
+
<div class="pe-y-label">Sequence position <em>(pos ↓)</em></div>
|
| 143 |
+
<div style="flex:1;position:relative;">
|
| 144 |
+
<canvas id="pe-canvas-angle" style="width:100%;display:block;cursor:crosshair;"></canvas>
|
| 145 |
+
<div class="tooltip" id="tt-ang"></div>
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
<div style="margin-left:28px">
|
| 149 |
+
<div class="pe-axis-label">Dimension pair index <em>(i →)</em></div>
|
| 150 |
+
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--color-text-secondary);margin-top:2px"><span>0 (fast, large θ)</span><span>64</span><span>127 (slow, tiny θ)</span></div>
|
| 151 |
+
</div>
|
| 152 |
+
<div class="pe-legend" style="margin-left:28px">
|
| 153 |
+
<span>0 rad</span>
|
| 154 |
+
<canvas id="legend-bar-ang" height="12" style="flex:1;border-radius:3px;"></canvas>
|
| 155 |
+
<span id="ang-max-label">max rad</span>
|
| 156 |
+
</div>
|
| 157 |
+
</div>
|
| 158 |
+
|
| 159 |
+
<div class="annotation-box" id="ann-box"></div>
|
| 160 |
+
</div>
|
| 161 |
+
|
| 162 |
+
<script>
|
| 163 |
+
const ROWS = 50, COLS = 128, D_MODEL = 256;
|
| 164 |
+
const isDark = matchMedia('(prefers-color-scheme: dark)').matches;
|
| 165 |
+
|
| 166 |
+
function theta(i) { return Math.pow(10000, -(2 * i) / D_MODEL); }
|
| 167 |
+
|
| 168 |
+
const cosMatrix = [], sinMatrix = [], ilMatrix = [], angMatrix = [];
|
| 169 |
+
const maxAngle = (ROWS - 1) * theta(0);
|
| 170 |
+
|
| 171 |
+
for (let r = 0; r < ROWS; r++) {
|
| 172 |
+
cosMatrix[r] = []; sinMatrix[r] = []; ilMatrix[r] = []; angMatrix[r] = [];
|
| 173 |
+
for (let c = 0; c < COLS; c++) {
|
| 174 |
+
const phi = r * theta(c);
|
| 175 |
+
cosMatrix[r][c] = Math.cos(phi);
|
| 176 |
+
sinMatrix[r][c] = Math.sin(phi);
|
| 177 |
+
angMatrix[r][c] = phi;
|
| 178 |
+
}
|
| 179 |
+
for (let d = 0; d < 256; d++) {
|
| 180 |
+
const pair = Math.floor(d / 2), phi = r * theta(pair);
|
| 181 |
+
ilMatrix[r][d] = (d % 2 === 0) ? Math.cos(phi) : Math.sin(phi);
|
| 182 |
+
}
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
// Diverging color for trig values [-1, 1]: green for positive, purple for negative
|
| 186 |
+
function trig2color(v, alpha) {
|
| 187 |
+
const t = (v + 1) / 2;
|
| 188 |
+
if (isDark) {
|
| 189 |
+
if (t > 0.5) {
|
| 190 |
+
const s = (t - 0.5) * 2;
|
| 191 |
+
return `rgba(${Math.round(20+s*10)},${Math.round(60+s*150)},${Math.round(70+s*60)},${alpha})`;
|
| 192 |
+
} else {
|
| 193 |
+
const s = (0.5 - t) * 2;
|
| 194 |
+
return `rgba(${Math.round(40+s*120)},${Math.round(20+s*20)},${Math.round(70+s*150)},${alpha})`;
|
| 195 |
+
}
|
| 196 |
+
} else {
|
| 197 |
+
if (t > 0.5) {
|
| 198 |
+
const s = (t - 0.5) * 2;
|
| 199 |
+
return `rgba(${Math.round(245-s*200)},${Math.round(252-s*70)},${Math.round(240-s*170)},${alpha})`;
|
| 200 |
+
} else {
|
| 201 |
+
const s = (0.5 - t) * 2;
|
| 202 |
+
return `rgba(${Math.round(235-s*50)},${Math.round(225-s*170)},${Math.round(252-s*30)},${alpha})`;
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
// Sequential amber for angle values [0, maxAngle]
|
| 208 |
+
function angle2color(v, alpha) {
|
| 209 |
+
const t = Math.min(v / maxAngle, 1);
|
| 210 |
+
if (isDark) {
|
| 211 |
+
return `rgba(${Math.round(15+t*230)},${Math.round(15+t*150)},${Math.round(15+t*10)},${alpha})`;
|
| 212 |
+
} else {
|
| 213 |
+
return `rgba(${Math.round(252-t*30)},${Math.round(252-t*145)},${Math.round(252-t*235)},${alpha})`;
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
function drawCanvas(canvas, matrix, colorFn, cols, hc, hr) {
|
| 218 |
+
const w = canvas.offsetWidth;
|
| 219 |
+
if (!w) return;
|
| 220 |
+
const rows = matrix.length;
|
| 221 |
+
const h = Math.round(w * (rows / cols) * 1.4);
|
| 222 |
+
const dpr = window.devicePixelRatio || 1;
|
| 223 |
+
canvas.width = w * dpr; canvas.height = h * dpr;
|
| 224 |
+
canvas.style.height = h + 'px';
|
| 225 |
+
const ctx = canvas.getContext('2d');
|
| 226 |
+
ctx.scale(dpr, dpr);
|
| 227 |
+
const cw = w / cols, ch = h / rows;
|
| 228 |
+
const dim = hc >= 0 || hr >= 0;
|
| 229 |
+
for (let r = 0; r < rows; r++) {
|
| 230 |
+
for (let c = 0; c < cols; c++) {
|
| 231 |
+
const isHL = (hc >= 0 && c === hc) || (hr >= 0 && r === hr);
|
| 232 |
+
ctx.fillStyle = colorFn(matrix[r][c], dim && !isHL ? 0.28 : 1);
|
| 233 |
+
ctx.fillRect(c * cw, r * ch, cw + 0.5, ch + 0.5);
|
| 234 |
+
}
|
| 235 |
+
}
|
| 236 |
+
if (hc >= 0) { ctx.strokeStyle='#EF9F27'; ctx.lineWidth=2; ctx.strokeRect(hc*cw+1,1,cw-1,rows*ch-2); }
|
| 237 |
+
if (hr >= 0) { ctx.strokeStyle='#1D9E75'; ctx.lineWidth=2; ctx.strokeRect(1,hr*ch+1,cols*cw-2,ch-1); }
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
function drawLegend(id, colorFn, minV, maxV) {
|
| 241 |
+
const lb = document.getElementById(id); if (!lb) return;
|
| 242 |
+
const lctx = lb.getContext('2d');
|
| 243 |
+
const w = lb.offsetWidth || 300, dpr = window.devicePixelRatio || 1;
|
| 244 |
+
lb.width = w * dpr; lb.style.width = '100%';
|
| 245 |
+
for (let x = 0; x < w; x++) {
|
| 246 |
+
lctx.fillStyle = colorFn(minV + (x / w) * (maxV - minV), 1);
|
| 247 |
+
lctx.fillRect(x * dpr, 0, dpr, 12);
|
| 248 |
+
}
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
let currentView = 'sidebyside', mode = 'cell';
|
| 252 |
+
let hlCol = -1, hlRow = -1;
|
| 253 |
+
|
| 254 |
+
function redraw(hc, hr) {
|
| 255 |
+
if (hc !== undefined) { hlCol = hc; hlRow = hr; }
|
| 256 |
+
const hc_ = hlCol, hr_ = hlRow;
|
| 257 |
+
if (currentView === 'sidebyside') {
|
| 258 |
+
drawCanvas(document.getElementById('pe-canvas-cos'), cosMatrix, trig2color, COLS, hc_, hr_);
|
| 259 |
+
drawCanvas(document.getElementById('pe-canvas-sin'), sinMatrix, trig2color, COLS, hc_, hr_);
|
| 260 |
+
drawLegend('legend-bar-sbs', trig2color, -1, 1);
|
| 261 |
+
} else if (currentView === 'interleaved') {
|
| 262 |
+
const ilHc = hc_ >= 0 ? hc_ * 2 : -1;
|
| 263 |
+
drawCanvas(document.getElementById('pe-canvas-interleaved'), ilMatrix, trig2color, 256, ilHc, hr_);
|
| 264 |
+
drawLegend('legend-bar-il', trig2color, -1, 1);
|
| 265 |
+
} else {
|
| 266 |
+
drawCanvas(document.getElementById('pe-canvas-angle'), angMatrix, angle2color, COLS, hc_, hr_);
|
| 267 |
+
drawLegend('legend-bar-ang', angle2color, 0, maxAngle);
|
| 268 |
+
document.getElementById('ang-max-label').textContent = maxAngle.toFixed(1) + ' rad';
|
| 269 |
+
}
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
function clearHL() { hlCol = -1; hlRow = -1; redraw(); }
|
| 273 |
+
|
| 274 |
+
function setView(v) {
|
| 275 |
+
currentView = v;
|
| 276 |
+
['sidebyside','interleaved','angle'].forEach(k => {
|
| 277 |
+
document.getElementById('view-'+k).style.display = k===v ? '' : 'none';
|
| 278 |
+
document.getElementById('btn-view-'+k).classList.toggle('active', k===v);
|
| 279 |
+
});
|
| 280 |
+
clearHL(); updateAnnotation();
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
function setMode(m) {
|
| 284 |
+
mode = m;
|
| 285 |
+
['cell','col','row'].forEach(k => document.getElementById('btn-'+k).classList.toggle('active', k===m));
|
| 286 |
+
clearHL();
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
function attachHover(canvasId, ttId, colsOverride, getInfo) {
|
| 290 |
+
const canvas = document.getElementById(canvasId);
|
| 291 |
+
const tt = document.getElementById(ttId);
|
| 292 |
+
canvas.addEventListener('mousemove', function(e) {
|
| 293 |
+
const rect = canvas.getBoundingClientRect();
|
| 294 |
+
const x = e.clientX - rect.left, y = e.clientY - rect.top;
|
| 295 |
+
const cols = colsOverride || COLS;
|
| 296 |
+
const col = Math.floor(x / (rect.width / cols));
|
| 297 |
+
const row = Math.floor(y / (rect.height / ROWS));
|
| 298 |
+
if (col < 0 || col >= cols || row < 0 || row >= ROWS) return;
|
| 299 |
+
const info = getInfo(row, col);
|
| 300 |
+
if (mode === 'cell') { redraw(-1, -1); tt.innerHTML = info.cell; }
|
| 301 |
+
else if (mode === 'col') { redraw(info.pairCol, -1); tt.innerHTML = info.colTip; }
|
| 302 |
+
else { redraw(-1, row); tt.innerHTML = info.rowTip; }
|
| 303 |
+
tt.style.left = Math.min(x+10, rect.width-250)+'px';
|
| 304 |
+
tt.style.top = Math.max(y-44,0)+'px';
|
| 305 |
+
tt.style.opacity = '1';
|
| 306 |
+
});
|
| 307 |
+
canvas.addEventListener('mouseleave', () => { tt.style.opacity='0'; clearHL(); });
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
function pairInfo(row, col) {
|
| 311 |
+
const phi = (row * theta(col)).toFixed(4);
|
| 312 |
+
const cv = Math.cos(row*theta(col)).toFixed(4);
|
| 313 |
+
const sv = Math.sin(row*theta(col)).toFixed(4);
|
| 314 |
+
const th = theta(col).toExponential(3);
|
| 315 |
+
const speed = col < 10 ? 'fast' : col < 60 ? 'moderate' : 'slow';
|
| 316 |
+
return { phi, cv, sv, th, speed };
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
attachHover('pe-canvas-cos', 'tt-cos', null, (row, col) => {
|
| 320 |
+
const { phi, cv, sv, th, speed } = pairInfo(row, col);
|
| 321 |
+
return {
|
| 322 |
+
pairCol: col,
|
| 323 |
+
cell: `<b>pos=${row}, pair i=${col}</b><br>cos(φ) = ${cv}<br>sin(φ) = ${sv}<br>φ = ${phi} rad`,
|
| 324 |
+
colTip: `<b>Pair i=${col}</b> (${speed}) · θ = ${th}<br>cos col highlighted left, sin col right`,
|
| 325 |
+
rowTip: `<b>pos=${row}</b> · full rotation vector (all pairs)`
|
| 326 |
+
};
|
| 327 |
+
});
|
| 328 |
+
|
| 329 |
+
attachHover('pe-canvas-sin', 'tt-sin', null, (row, col) => {
|
| 330 |
+
const { phi, cv, sv, th, speed } = pairInfo(row, col);
|
| 331 |
+
return {
|
| 332 |
+
pairCol: col,
|
| 333 |
+
cell: `<b>pos=${row}, pair i=${col}</b><br>sin(φ) = ${sv}<br>cos(φ) = ${cv}<br>φ = ${phi} rad`,
|
| 334 |
+
colTip: `<b>Pair i=${col}</b> (${speed}) · θ = ${th}`,
|
| 335 |
+
rowTip: `<b>pos=${row}</b> · full rotation vector (all pairs)`
|
| 336 |
+
};
|
| 337 |
+
});
|
| 338 |
+
|
| 339 |
+
attachHover('pe-canvas-interleaved', 'tt-il', 256, (row, d) => {
|
| 340 |
+
const pair = Math.floor(d / 2), isCos = d % 2 === 0;
|
| 341 |
+
const { phi, cv, sv, th, speed } = pairInfo(row, pair);
|
| 342 |
+
return {
|
| 343 |
+
pairCol: d,
|
| 344 |
+
cell: `<b>pos=${row}, d=${d}</b> (pair ${pair}, ${isCos?'cos':'sin'})<br>value = ${isCos?cv:sv}<br>φ = ${phi} rad`,
|
| 345 |
+
colTip: `<b>d=${d}</b> · pair ${pair} · ${isCos?'<b style="color:#1D9E75">cos</b>':'<b style="color:#BA7517">sin</b>'} channel`,
|
| 346 |
+
rowTip: `<b>pos=${row}</b> · interleaved rotation vector (256 dims)`
|
| 347 |
+
};
|
| 348 |
+
});
|
| 349 |
+
|
| 350 |
+
attachHover('pe-canvas-angle', 'tt-ang', null, (row, col) => {
|
| 351 |
+
const { phi, cv, sv, th, speed } = pairInfo(row, col);
|
| 352 |
+
return {
|
| 353 |
+
pairCol: col,
|
| 354 |
+
cell: `<b>pos=${row}, pair i=${col}</b><br>φ = ${phi} rad<br>→ cos = ${cv}, sin = ${sv}`,
|
| 355 |
+
colTip: `<b>Pair i=${col}</b> (${speed}) · θ = ${th}<br>angle grows by θ per position step`,
|
| 356 |
+
rowTip: `<b>pos=${row}</b> · angle = pos × θ<sub>i</sub> for each pair`
|
| 357 |
+
};
|
| 358 |
+
});
|
| 359 |
+
|
| 360 |
+
function updateAnnotation() {
|
| 361 |
+
const ann = document.getElementById('ann-box');
|
| 362 |
+
if (currentView === 'sidebyside') {
|
| 363 |
+
ann.innerHTML = `<div class="ann-row">
|
| 364 |
+
<div class="ann-col">
|
| 365 |
+
<div class="ann-head" style="color:#0F6E56">cos matrix (left) — the "stay" component</div>
|
| 366 |
+
cos(pos·θ<sub>i</sub>) scales how much of the original vector survives. At φ=0 it is 1 (identity). At φ=π it is −1 (full flip). Fast pairs (left columns) cycle rapidly; slow pairs (right) stay near 1 across all 50 positions.
|
| 367 |
+
</div>
|
| 368 |
+
<div class="ann-col">
|
| 369 |
+
<div class="ann-head" style="color:#854F0B">sin matrix (right) — the "spin" component</div>
|
| 370 |
+
sin(pos·θ<sub>i</sub>) scales the 90°-rotated copy <code>qw2 = [−q<sub>odd</sub>, q<sub>even</sub>]</code>. At φ=0 it is 0 (no spin); at φ=π/2 it is 1 (maximum rotation contribution). Together cos² + sin² = 1, preserving vector length exactly.
|
| 371 |
+
</div>
|
| 372 |
+
</div>
|
| 373 |
+
<hr class="divider">
|
| 374 |
+
<div style="font-size:11px"><b>Full rotation formula:</b> q<sub>new</sub> = q · cos_pos + [−q<sub>odd</sub>, q<sub>even</sub>] · sin_pos</div>`;
|
| 375 |
+
} else if (currentView === 'interleaved') {
|
| 376 |
+
ann.innerHTML = `<div class="ann-row">
|
| 377 |
+
<div class="ann-col">
|
| 378 |
+
<div class="ann-head">How the code broadcasts</div>
|
| 379 |
+
<code>cos_pos = repeat(sinusoidal[..., 1::2], rep=2)</code> duplicates each cos value twice, so both dims of a pair share the same scalar.<br>
|
| 380 |
+
<code>sin_pos = repeat(sinusoidal[..., ::2], rep=2)</code> does the same for sin.<br><br>
|
| 381 |
+
Each consecutive pair of columns (d, d+1) gets the same rotation angle — only the trig function differs.
|
| 382 |
+
</div>
|
| 383 |
+
<div class="ann-col">
|
| 384 |
+
<div class="ann-head">What to notice</div>
|
| 385 |
+
Adjacent columns are always identical in value (the repeat). Visually this makes each pair look like a 2-pixel-wide stripe. Fast pairs (left) stripe rapidly across positions; slow pairs (right) are nearly uniform. The pattern <em>is</em> the side-by-side view — just interleaved instead of separated.
|
| 386 |
+
</div>
|
| 387 |
+
</div>`;
|
| 388 |
+
} else {
|
| 389 |
+
ann.innerHTML = `<div class="ann-row">
|
| 390 |
+
<div class="ann-col">
|
| 391 |
+
<div class="ann-head">The underlying clock: φ = pos · θ<sub>i</sub></div>
|
| 392 |
+
Both cos and sin are simply read off the unit circle at this angle. The left column (pair 0) reaches ${maxAngle.toFixed(1)} rad by pos=49 — many full revolutions. The right column barely ticks above 0. This triangular ramp is the speed spectrum that gives RoPE its multi-scale position sensitivity.
|
| 393 |
+
</div>
|
| 394 |
+
<div class="ann-col">
|
| 395 |
+
<div class="ann-head">Why the gradient is triangular</div>
|
| 396 |
+
φ = pos × θ<sub>i</sub>, where θ<sub>i</sub> = 10000<sup>−2i/d</sup> decreases geometrically rightward and pos increases linearly downward. The brightest cell is bottom-left (pos=49, pair 0). Moving right, the brightness drops exponentially; moving up, it drops linearly. This is the "clock" RoPE winds at different speeds per pair.
|
| 397 |
+
</div>
|
| 398 |
+
</div>`;
|
| 399 |
+
}
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
setTimeout(() => { redraw(); updateAnnotation(); }, 60);
|
| 403 |
+
window.addEventListener('resize', () => redraw());
|
| 404 |
+
</script>
|
| 405 |
+
</body>
|
| 406 |
</html>
|