File size: 8,391 Bytes
43b947d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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

const $=id=>document.getElementById(id);
const esc=s=>String(s).replace(/[&<>"]/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c]));
async function api(p,body){const r=await fetch(p,{method:"POST",
 headers:{"Content-Type":"application/json"},body:JSON.stringify(body)});return r.json()}
function stats(el,d){el.style.display="grid";
 const s=[["stream tokens",d.T.toLocaleString()],["time",d.wall_s+" s"],
  ["&mu;s/token",d.us_per_token],["cells",d.cells.toLocaleString()],["recall",d.acc]];
 el.innerHTML=s.map(x=>`<div class="stat"><b>${x[1]}</b><span>${x[0]}</span></div>`).join("")}
/* section 01 */
const DEF=[["the vault password","swordfish","bluedragon"],
 ["Elbaz's first name","Eric",""],["the car's color","red",""],
 ["the meeting time","3pm","4pm"],["the favorite meal","stew",""]];
function prow(n,v,nv){const d=document.createElement("div");d.className="prow";
 d.innerHTML=`<input placeholder="name" value="${esc(n)}">
  <input placeholder="value" value="${esc(v)}">
  <input placeholder="new value (optional)" value="${esc(nv)}">
  <button class="del" title="remove">&times;</button>`;
 d.querySelector(".del").onclick=()=>d.remove();return d}
DEF.forEach(x=>$("pairs").appendChild(prow(...x)));
$("addp").onclick=()=>$("pairs").appendChild(prow("","",""));
$("runc").onclick=async()=>{$("runc").disabled=true;$("runc").textContent="reading your stream&hellip;";
 try{
  const pairs=[...document.querySelectorAll("#pairs .prow")].map(r=>{
   const i=r.querySelectorAll("input");return{name:i[0].value.trim(),value:i[1].value.trim(),
    new_value:i[2].value.trim()}}).filter(p=>p.name&&p.value);
  const d=await api("/api/custom",{text:$("doc").value,pairs,seed:(Math.random()*1e6)|0});
  if(d.error){alert(d.error);return}
  $("meta").textContent=`capacity8k.pt · scale ${d.scale||96} · ${d.device} · run ${d.ts}`;
  stats($("stc"),d);
  $("qrt").innerHTML=`<tr><th>asked</th><th>the model answered</th><th>latest value</th><th></th></tr>`+
   d.rows.map(r=>`<tr><td><b>${esc(r.name)}</b></td>
    <td class="${r.ok?"y":"n"}">${esc(r.asked)}</td>
    <td class="${r.updated?"updv":""}">${esc(r.truth)}</td>
    <td>${r.updated?`<span class="pill">was ${esc(r.old)}</span>`:""}${r.ok?"":" ✗"}</td></tr>`).join("");
  $("cres").style.display="block";
  $("ccap").innerHTML=`${d.n_pairs} of your facts stored in ${d.T.toLocaleString()} tokens of your text`+
   (d.n_upd?`, ${d.n_upd} overwritten mid-stream`:"")+` · recall <b>${d.acc}</b> · ${d.us_per_token} &mu;s/token`+
   ` · key ids: `+Object.entries(d.keymap).slice(0,4).map(([n,k])=>`${esc(n)}=${k}`).join(", ")+`&hellip;`;
 }catch(e){alert(e)}$("runc").disabled=false;$("runc").textContent="Read, store, overwrite — and answer"};
/* section 02 */
$("np").oninput=e=>$("npv").textContent=e.target.value;
$("fl").oninput=e=>$("flv").textContent=e.target.value;
$("ow").oninput=e=>$("owv").textContent=e.target.value+"%";
function drawLatt(pts,sc){const c=$("latt"),x=c.getContext("2d");
 c.width=c.clientWidth*2;c.height=560;x.scale(2,2);const W=c.width/2,H=280;
 x.fillStyle="#fff";x.fillRect(0,0,W,H);
 x.strokeStyle="#eceef2";for(let i=0;i<=8;i++){const p=i/8;
  x.beginPath();x.moveTo(p*W,0);x.lineTo(p*W,H);x.stroke();
  x.beginPath();x.moveTo(0,p*H);x.lineTo(W,p*H);x.stroke()}
 x.fillStyle="#2547d0";for(const p of pts){const u=(p[0]/sc+1)/2,v=(p[1]/sc+1)/2;
  x.beginPath();x.arc(u*W,v*H,1.7,0,7);x.fill()}}
$("run").onclick=async()=>{$("run").disabled=true;$("run").textContent="streaming&hellip;";
 try{const d=await api("/api/store",{n_pairs:+$("np").value,filler:+$("fl").value,
  overwrite_frac:+$("ow").value/100,seed:(Math.random()*1e6)|0});
  $("meta").textContent=`capacity8k.pt · scale ${d.scale} · ${d.params.toLocaleString()} params · ${d.device} · run ${d.ts}`;
  stats($("st"),d);drawLatt(d.addr_points,d.scale);
  $("qr").innerHTML=`<tr><th>query key</th><th>answered</th><th>true</th><th></th></tr>`+
   d.rows.map(r=>`<tr><td>${r.key}</td><td class="${r.ok?"y":"n"}">${r.pred}</td>`+
   `<td class="${r.updated?"updv":""}">${r.gold}</td>`+
   `<td>${r.updated?'<span class="pill">overwritten</span>':""}${r.ok?"":" ✗"}</td></tr>`).join("");
 }catch(e){alert(e)}$("run").disabled=false;$("run").textContent="stream & query"};
/* section 03 */
function drawCC(data){const c=$("cc"),x=c.getContext("2d");c.width=c.clientWidth*2;c.height=600;
 x.scale(2,2);const W=c.width/2,H=300,P=48;
 const maxT=Math.max(...data.map(d=>d.T)),us=data.map(d=>d.us_per_token),
  ce=data.map(d=>d.cells),maxU=Math.max(...us)*1.25,maxC=Math.max(...ce)*1.15,
  maxA=Math.max(...data.map(d=>d.flops_attention_equiv));
 const X=t=>P+(Math.log(t)/Math.log(maxT))*(W-P-16),
  Y=u=>H-30-(u/maxU)*(H-56),Y2=v=>H-30-(v/maxC)*(H-56),YA=v=>H-30-(v/maxA)*(H-56);
 x.fillStyle="#fff";x.fillRect(0,0,W,H);x.strokeStyle="#eceef2";x.fillStyle="#6b7280";
 x.font="11px -apple-system,sans-serif";
 for(let i=0;i<=4;i++){const px=P+(i/4)*(W-P-16);x.beginPath();x.moveTo(px,20);x.lineTo(px,H-30);x.stroke()}
 x.strokeStyle="#c2413b";x.setLineDash([6,5]);x.beginPath();let f1=true;
 for(const d of data){const px=X(d.T),py=YA(d.flops_attention_equiv);
  f1?x.moveTo(px,py):x.lineTo(px,py);f1=false}x.stroke();x.setLineDash([]);
 x.strokeStyle="#2547d0";x.lineWidth=2.4;x.beginPath();f1=true;
 for(const d of data){const px=X(d.T),py=Y(d.us_per_token);f1?x.moveTo(px,py):x.lineTo(px,py);f1=false}
 x.stroke();
 x.strokeStyle="#0e8a5f";x.lineWidth=2.4;x.beginPath();f1=true;
 for(const d of data){const px=X(d.T),py=Y2(d.cells);f1?x.moveTo(px,py):x.lineTo(px,py);f1=false}
 x.stroke();x.lineWidth=1;
 for(const d of data){x.fillStyle="#2547d0";x.beginPath();x.arc(X(d.T),Y(d.us_per_token),3.4,0,7);x.fill();
  x.fillStyle="#0e8a5f";x.beginPath();x.arc(X(d.T),Y2(d.cells),3.4,0,7);x.fill()}
 x.fillStyle="#6b7280";
 for(const d of data)x.fillText(d.T.toLocaleString(),X(d.T)-16,H-14);
 x.fillText("stream length (tokens, log scale)",W/2-80,H-2);
 x.fillText("&mu;s/token (blue, flat = constant) · cells (green) · attention O(T²) (dashed red)",P,16)}
$("cost").onclick=async()=>{$("cost").disabled=true;$("cost").textContent="measuring&hellip;";
 try{const sizes=[64,256,1024,2048,4096,8192].map(n=>({n_pairs:n,filler:14}));
  const d=await api("/api/cost",{sizes});drawCC(d)}catch(e){alert(e)}
 $("cost").disabled=false;$("cost").textContent="measure the cost curve"};
/* section 04 */
$("cv").onclick=async()=>{$("cv").disabled=true;
 try{const d=await api("/api/cellview",{name:$("cvn").value.trim()||"key",
  value:$("cvv").value.trim()||"v1",new_value:$("cvnv").value.trim(),seed:(Math.random()*1e6)|0});
  if(d.error){alert(d.error);return}
  const inv={[d.vid]:d.value};inv[d.nid]=d.new_value||d.value;
  $("cvs").innerHTML=`
   <div class="stats" style="display:grid">
    <div class="stat"><b>(${d.addr[0]}, ${d.addr[1]})</b><span>learned address</span></div>
    <div class="stat"><b>${esc(inv[d.answers.before] ?? "—")}</b><span>stored &amp; retrieved before</span></div>
    <div class="stat"><b class="${d.answers.after!==null&&d.answers.after!==d.answers.before?"updv":""}">${esc(d.answers.after!==null?inv[d.answers.after]:"—")}</b><span>after overwrite, same cells</span></div>
   </div>
   <p class="note" style="margin:8px 0 0">key id ${d.kid} &rarr; address on grid scale ${d.scale};
   the write lands as <code>W &larr; &alpha;W + &beta;(v<sub>new</sub> &minus; &alpha;W&kappa;)&kappa;<sup>T</sup>:
   the old association is subtracted out, the new one takes its place &mdash; nothing is appended.</p>`;
  drawCell(d)}catch(e){alert(e)}$("cv").disabled=false};
function drawCell(d){const c=$("cellcv"),x=c.getContext("2d");
 c.width=c.clientWidth*2;c.height=600;x.scale(2,2);const W=c.width/2,H=300;
 x.fillStyle="#fff";x.fillRect(0,0,W,H);const cw=W/3,ch=H/3;
 for(let i=0;i<3;i++)for(let j=0;j<3;j++){
  const phi=d.cells[i][j].phi,z=d.cells[i][j].z;
  const t=Math.min(1,phi/0.7);
  x.fillStyle=`rgba(37,71,208,${(0.06+0.85*t).toFixed(3)})`;
  x.fillRect(j*cw+6,i*ch+6,cw-12,ch-12);
  x.fillStyle=t>0.45?"#fff":"#6b7280";x.font="12px -apple-system,sans-serif";
  x.fillText(`(${z[0]}, ${z[1]})`,j*cw+14,i*ch+24);
  x.font="600 15px -apple-system,sans-serif";
  x.fillText(`phi ${phi.toFixed(3)}`,j*cw+14,i*ch+ch-18)}
 $("cvcap").textContent="the 3x3 neighborhood of the key's address; darker = stronger write/read weight (the bump kernel)."}
window.addEventListener("resize",()=>{});