everydaytok commited on
Commit
c8ba274
Β·
verified Β·
1 Parent(s): 56e8396

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +102 -45
index.html CHANGED
@@ -57,7 +57,8 @@
57
  <span id="b-data" class="px-1.5 py-0.5 rounded bg-pink-900/60 text-pink-300 border border-pink-800/60">HOUSNG</span>
58
  </div>
59
  <div class="flex items-center gap-2 ml-1">
60
- <span id="lat-lbl" class="text-[7px] text-slate-700">Β±0</span>
 
61
  <span id="q-lbl" class="text-[8px] text-slate-600">Q:0</span>
62
  <div id="run-dot" class="w-2 h-2 rounded-full bg-slate-700"></div>
63
  <button onclick="openDrawer()" class="text-[10px] bg-blue-700 hover:bg-blue-600 px-2 py-1 rounded font-bold">βš™ DIALS</button>
@@ -137,11 +138,12 @@
137
  <div class="col-span-2 bg-slate-900 rounded p-3 border border-violet-900/50">
138
  <div class="text-violet-400 text-[9px] font-bold mb-1">
139
  CROSS-CONNECT
140
- <span class="text-slate-600 font-normal ml-1">lateral springs between adjacent dimensions</span>
141
  </div>
142
  <div class="text-[9px] text-slate-500 mb-2">
143
- OFF β†’ n independent parallel hourglasses<br>
144
- ON β†’ genuine nD mesh: tension in dim 1 propagates into dim 2
 
145
  </div>
146
  <button id="drawer-cross-btn" onclick="toggleCross()"
147
  class="w-full py-2 rounded text-xs font-bold border border-slate-700 bg-slate-800 text-slate-400">
@@ -254,44 +256,62 @@
254
  const cfg = { mode: 'training', architecture: 'additive' };
255
  const topo = { inputs: 1, upper: 3, lower: 3 };
256
  let crossConnect = false;
 
257
 
258
  // ── CROSS CONNECT ─────────────────────────────────────────────────────────────
259
  async function toggleCross() {
260
  const res = await fetch('/toggle_cross', { method: 'POST' });
261
  const data = await res.json();
262
  crossConnect = data.cross_connect;
 
263
  updateCrossUI();
264
  meshPlotted = false;
265
  }
266
 
267
  function updateCrossUI() {
268
- const btn1 = document.getElementById('b-cross');
269
- const btn2 = document.getElementById('drawer-cross-btn');
270
- const info = document.getElementById('cross-info');
 
 
 
 
 
 
 
 
271
  if (crossConnect) {
272
  btn1.className = 'px-1.5 py-0.5 rounded border font-bold text-[8px] transition-all bg-violet-800 text-violet-200 border-violet-600';
273
  btn1.innerText = 'CROSS:ON';
274
  btn2.className = 'w-full py-2 rounded text-xs font-bold border border-violet-600 bg-violet-900 text-violet-200';
275
  btn2.innerText = 'CROSS-CONNECT: ON (click to disable)';
276
- const n = topo.inputs, u = topo.upper, l = topo.lower;
277
- const lat = (n-1) * (u + l);
278
- info.innerText = `${lat} lateral spring${lat!==1?'s':''} active β€” true ${n}D mesh`;
 
 
279
  } else {
280
  btn1.className = 'px-1.5 py-0.5 rounded border font-bold text-[8px] transition-all bg-slate-900 text-slate-600 border-slate-700';
281
  btn1.innerText = 'CROSS:OFF';
282
  btn2.className = 'w-full py-2 rounded text-xs font-bold border border-slate-700 bg-slate-800 text-slate-400';
283
  btn2.innerText = 'CROSS-CONNECT: OFF (click to enable)';
284
- info.innerText = `${topo.inputs} independent parallel hourglasses`;
 
285
  }
286
  }
287
 
288
  // ── TOPOLOGY ──────────────────────────────────────────────────────────────────
289
  function updateSpringCount() {
290
  const { inputs: n, upper: u, lower: l } = topo;
291
- const vert = n * (2*u + 2*l);
292
- const lat = crossConnect ? (n-1)*(u+l) : 0;
 
 
 
 
 
293
  document.getElementById('spring-count').innerText =
294
- `${vert} vertical + ${lat} lateral = ${vert+lat} springs`;
295
  document.getElementById('custom-dim-hint').innerText =
296
  n === 1 ? '(single value)' : `(${n} values, comma-separated)`;
297
  updateCrossUI();
@@ -454,23 +474,29 @@ function buildPos(layers, n_inputs, n_upper, n_lower) {
454
  const halfSp = COL_W * (n_inputs - 1) / 2;
455
  const bSprd = n_inputs === 1 ? 3.8 : Math.min(COL_W * 0.55, 1.8);
456
 
 
 
 
 
457
  layers.forEach((layer, li) => {
458
  const y = Y[li];
459
  layer.forEach(nid => {
460
  const kind = nid[0];
461
- let dim = 1;
 
462
  if ('ABC'.includes(kind)) {
463
  dim = parseInt(nid.slice(1));
464
  } else {
465
- dim = parseInt(nid.slice(1).split('_')[0]);
 
 
 
466
  }
467
  const cx = n_inputs === 1 ? 0 : -halfSp + (dim-1)*COL_W;
468
  if ('ABC'.includes(kind)) {
469
  pos[nid] = [cx, y];
470
  } else {
471
- const j = parseInt(nid.slice(1).split('_')[1]);
472
- const total = kind === 'U' ? n_upper : n_lower;
473
- const t = total === 1 ? 0 : (2*(j-1)/(total-1) - 1);
474
  pos[nid] = [cx + bSprd * t, y];
475
  }
476
  });
@@ -494,42 +520,58 @@ function busShapes(pos, n_inputs) {
494
  return sh;
495
  }
496
 
497
- function springColor(k, isLateral) {
498
- // Lateral springs shown in violet to distinguish from vertical
499
- if (isLateral) {
500
- const t = Math.min(Math.abs(k) / 6, 1);
501
- return [`rgb(${Math.round(120+t*80)},50,${Math.round(180+t*75)})`, 0.8 + t*2.5];
502
- }
503
  const t = Math.min(Math.abs(k) / 6, 1);
504
  if (k >= 0) return [`rgb(${Math.round(180+t*70)},${Math.round(100+t*80)},30)`, 1.0+t*3.5];
505
  return [`rgb(30,${Math.round(80+t*100)},${Math.round(140+t*115)})`, 1.0+t*3.5];
506
  }
507
 
508
- function isLateralKey(key) {
509
- // Lateral springs: both sides start with U or L, same kind
 
 
510
  const [u, v] = key.split('β†’');
511
- return u && v && 'UL'.includes(u[0]) && u[0] === v[0];
 
 
 
 
 
 
 
 
512
  }
513
 
514
  function buildTraces(nodes, springs, layers, n_inputs, n_upper, n_lower) {
515
  const pos = buildPos(layers, n_inputs, n_upper, n_lower);
516
  const traces = [];
517
 
518
- // Edges β€” lateral springs drawn after vertical so they appear on top
519
- const vertEdges = [], latEdges = [];
520
  for (const [key, k] of Object.entries(springs)) {
521
  const [u, v] = key.split('β†’');
522
  if (!pos[u] || !pos[v]) continue;
523
- (isLateralKey(key) ? latEdges : vertEdges).push([key, k, u, v]);
524
  }
525
 
526
- for (const [key, k, u, v] of [...vertEdges, ...latEdges]) {
527
- const lat = isLateralKey(key);
528
- const [col, wd] = springColor(k, lat);
529
  traces.push({
530
  type:'scatter', mode:'lines',
531
  x:[pos[u][0], pos[v][0]], y:[pos[u][1], pos[v][1]],
532
- line:{color:col, width:wd, dash: lat ? 'dot' : 'solid'},
 
 
 
 
 
 
 
 
 
 
 
 
533
  hoverinfo:'none', showlegend:false
534
  });
535
  }
@@ -543,6 +585,16 @@ function buildTraces(nodes, springs, layers, n_inputs, n_upper, n_lower) {
543
  };
544
  const isIO = id => 'ABC'.includes(id[0]);
545
 
 
 
 
 
 
 
 
 
 
 
546
  traces.push({
547
  type:'scatter', mode:'markers+text',
548
  x: allN.map(id => pos[id]?.[0] ?? 0),
@@ -556,13 +608,17 @@ function buildTraces(nodes, springs, layers, n_inputs, n_upper, n_lower) {
556
  marker:{
557
  size: allN.map(id => {
558
  const v = Math.abs(nodes[id]?.vel ?? 0);
559
- return (isIO(id) ? 18 : 10) + Math.min(v*30, 8);
 
560
  }),
561
- color: allN.map(id => NCOL(id)),
562
  opacity: allN.map(id => 0.75 + Math.min(Math.abs(nodes[id]?.vel??0)*1.8, 0.25)),
563
  line:{
564
- width:2.5,
565
- color: allN.map(id => nodes[id]?.anchored ? '#ef4444' : '#22c55e')
 
 
 
566
  }
567
  },
568
  hoverinfo:'none', showlegend:false
@@ -602,8 +658,8 @@ setInterval(async () => {
602
 
603
  syncTopoUI(d.n_inputs, d.n_upper, d.n_lower);
604
  crossConnect = d.cross_connect;
 
605
  updateCrossUI();
606
- document.getElementById('lat-lbl').innerText = `Β±${d.n_lateral}`;
607
  document.getElementById('b-alpha').innerText = `Ξ±:${d.back_alpha.toFixed(2)}`;
608
  document.getElementById('b-data').innerText = (d.dataset_type||'').slice(0,6).toUpperCase();
609
 
@@ -648,14 +704,15 @@ setInterval(async () => {
648
  }
649
  document.getElementById('pane-nodes').innerHTML = nh;
650
 
651
- // Springs pane β€” lateral springs shown with violet marker
652
  let sh = '';
653
  for (const [key, k] of Object.entries(d.springs)) {
654
- const lat = isLateralKey(key);
655
- const kc = lat ? 'text-violet-300' : (k < 0 ? 'text-blue-300' : k > 4 ? 'text-yellow-200' : 'text-purple-300');
656
- const pfx = lat ? '<span class="text-violet-600 mr-1">↔</span>' : '';
 
657
  sh += `<div class="flex justify-between py-0.5 border-b border-slate-900">
658
- ${pfx}<span class="text-slate-500 text-[9px]">${key}</span>
659
  <span class="${kc} font-bold text-[10px]">${k.toFixed(4)}</span></div>`;
660
  }
661
  document.getElementById('pane-springs').innerHTML = sh;
 
57
  <span id="b-data" class="px-1.5 py-0.5 rounded bg-pink-900/60 text-pink-300 border border-pink-800/60">HOUSNG</span>
58
  </div>
59
  <div class="flex items-center gap-2 ml-1">
60
+ <!-- shared-vertex counter, hidden when 0 -->
61
+ <span id="shared-lbl" class="text-[7px] text-slate-700"></span>
62
  <span id="q-lbl" class="text-[8px] text-slate-600">Q:0</span>
63
  <div id="run-dot" class="w-2 h-2 rounded-full bg-slate-700"></div>
64
  <button onclick="openDrawer()" class="text-[10px] bg-blue-700 hover:bg-blue-600 px-2 py-1 rounded font-bold">βš™ DIALS</button>
 
138
  <div class="col-span-2 bg-slate-900 rounded p-3 border border-violet-900/50">
139
  <div class="text-violet-400 text-[9px] font-bold mb-1">
140
  CROSS-CONNECT
141
+ <span class="text-slate-600 font-normal ml-1">structural shared vertices</span>
142
  </div>
143
  <div class="text-[9px] text-slate-500 mb-2">
144
+ OFF β†’ n independent parallel hourglasses (default)<br>
145
+ ON β†’ boundary hidden nodes that visually overlap become one<br>
146
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shared vertex with springs to both neighbouring dims
147
  </div>
148
  <button id="drawer-cross-btn" onclick="toggleCross()"
149
  class="w-full py-2 rounded text-xs font-bold border border-slate-700 bg-slate-800 text-slate-400">
 
256
  const cfg = { mode: 'training', architecture: 'additive' };
257
  const topo = { inputs: 1, upper: 3, lower: 3 };
258
  let crossConnect = false;
259
+ let nShared = 0;
260
 
261
  // ── CROSS CONNECT ─────────────────────────────────────────────────────────────
262
  async function toggleCross() {
263
  const res = await fetch('/toggle_cross', { method: 'POST' });
264
  const data = await res.json();
265
  crossConnect = data.cross_connect;
266
+ nShared = data.n_shared;
267
  updateCrossUI();
268
  meshPlotted = false;
269
  }
270
 
271
  function updateCrossUI() {
272
+ const btn1 = document.getElementById('b-cross');
273
+ const btn2 = document.getElementById('drawer-cross-btn');
274
+ const info = document.getElementById('cross-info');
275
+ const slbl = document.getElementById('shared-lbl');
276
+ const n = topo.inputs;
277
+ const u = topo.upper;
278
+ const l = topo.lower;
279
+ const ns = nShared || (crossConnect && n >= 2
280
+ ? (n-1) * ((u >= 2 ? 1 : 0) + (l >= 2 ? 1 : 0))
281
+ : 0);
282
+
283
  if (crossConnect) {
284
  btn1.className = 'px-1.5 py-0.5 rounded border font-bold text-[8px] transition-all bg-violet-800 text-violet-200 border-violet-600';
285
  btn1.innerText = 'CROSS:ON';
286
  btn2.className = 'w-full py-2 rounded text-xs font-bold border border-violet-600 bg-violet-900 text-violet-200';
287
  btn2.innerText = 'CROSS-CONNECT: ON (click to disable)';
288
+ info.innerText = ns
289
+ ? `${ns} shared vertex${ns !== 1 ? 'es' : ''} β€” boundary nodes merged`
290
+ : 'No merges (need Dβ‰₯2 and U/Lβ‰₯2)';
291
+ slbl.innerText = ns ? `βŠ•${ns}` : '';
292
+ slbl.className = 'text-[7px] text-violet-400';
293
  } else {
294
  btn1.className = 'px-1.5 py-0.5 rounded border font-bold text-[8px] transition-all bg-slate-900 text-slate-600 border-slate-700';
295
  btn1.innerText = 'CROSS:OFF';
296
  btn2.className = 'w-full py-2 rounded text-xs font-bold border border-slate-700 bg-slate-800 text-slate-400';
297
  btn2.innerText = 'CROSS-CONNECT: OFF (click to enable)';
298
+ info.innerText = `${n} independent parallel hourglass${n !== 1 ? 'es' : ''}`;
299
+ slbl.innerText = '';
300
  }
301
  }
302
 
303
  // ── TOPOLOGY ──────────────────────────────────────────────────────────────────
304
  function updateSpringCount() {
305
  const { inputs: n, upper: u, lower: l } = topo;
306
+ // With structural merge: each merge removes 2 springs and adds 2 (net=0),
307
+ // but removes 1 node. Spring count stays the same; node count reduces.
308
+ const ns = crossConnect && n >= 2
309
+ ? (n-1) * ((u >= 2 ? 1 : 0) + (l >= 2 ? 1 : 0))
310
+ : 0;
311
+ const totalSprings = n * (2*u + 2*l); // unchanged by merge
312
+ const totalNodes = 3*n + n*u + n*l - ns;
313
  document.getElementById('spring-count').innerText =
314
+ `${totalSprings} springs | ${totalNodes} nodes${ns ? ` (${ns} shared vertices)` : ''}`;
315
  document.getElementById('custom-dim-hint').innerText =
316
  n === 1 ? '(single value)' : `(${n} values, comma-separated)`;
317
  updateCrossUI();
 
474
  const halfSp = COL_W * (n_inputs - 1) / 2;
475
  const bSprd = n_inputs === 1 ? 3.8 : Math.min(COL_W * 0.55, 1.8);
476
 
477
+ // Build a flat lookup: nid β†’ layer index (to get Y)
478
+ const layerOf = {};
479
+ layers.forEach((layer, li) => layer.forEach(nid => { layerOf[nid] = li; }));
480
+
481
  layers.forEach((layer, li) => {
482
  const y = Y[li];
483
  layer.forEach(nid => {
484
  const kind = nid[0];
485
+ // Parse dim and j from node ID
486
+ let dim = 1, j = 1, total = 1;
487
  if ('ABC'.includes(kind)) {
488
  dim = parseInt(nid.slice(1));
489
  } else {
490
+ const parts = nid.slice(1).split('_');
491
+ dim = parseInt(parts[0]);
492
+ j = parseInt(parts[1]);
493
+ total = kind === 'U' ? n_upper : n_lower;
494
  }
495
  const cx = n_inputs === 1 ? 0 : -halfSp + (dim-1)*COL_W;
496
  if ('ABC'.includes(kind)) {
497
  pos[nid] = [cx, y];
498
  } else {
499
+ const t = total === 1 ? 0 : (2*(j-1)/(total-1) - 1);
 
 
500
  pos[nid] = [cx + bSprd * t, y];
501
  }
502
  });
 
520
  return sh;
521
  }
522
 
523
+ function springColor(k) {
 
 
 
 
 
524
  const t = Math.min(Math.abs(k) / 6, 1);
525
  if (k >= 0) return [`rgb(${Math.round(180+t*70)},${Math.round(100+t*80)},30)`, 1.0+t*3.5];
526
  return [`rgb(30,${Math.round(80+t*100)},${Math.round(140+t*115)})`, 1.0+t*3.5];
527
  }
528
 
529
+ // A spring is a "shared-vertex spring" if it crosses dimensions:
530
+ // e.g. A2β†’U1_3 (dim2 input β†’ dim1's canonical shared node).
531
+ // We detect this by checking if the input side dim differs from the node's dim.
532
+ function isSharedSpring(key) {
533
  const [u, v] = key.split('β†’');
534
+ if (!u || !v) return false;
535
+ const getDim = s => {
536
+ if ('ABC'.includes(s[0])) return parseInt(s.slice(1));
537
+ const p = s.slice(1).split('_'); return parseInt(p[0]);
538
+ };
539
+ try {
540
+ const du = getDim(u), dv = getDim(v);
541
+ return !isNaN(du) && !isNaN(dv) && du !== dv;
542
+ } catch { return false; }
543
  }
544
 
545
  function buildTraces(nodes, springs, layers, n_inputs, n_upper, n_lower) {
546
  const pos = buildPos(layers, n_inputs, n_upper, n_lower);
547
  const traces = [];
548
 
549
+ // Draw shared-vertex springs on top (after normal springs)
550
+ const normalEdges = [], sharedEdges = [];
551
  for (const [key, k] of Object.entries(springs)) {
552
  const [u, v] = key.split('β†’');
553
  if (!pos[u] || !pos[v]) continue;
554
+ (isSharedSpring(key) ? sharedEdges : normalEdges).push([key, k, u, v]);
555
  }
556
 
557
+ for (const [key, k, u, v] of normalEdges) {
558
+ const [col, wd] = springColor(k);
 
559
  traces.push({
560
  type:'scatter', mode:'lines',
561
  x:[pos[u][0], pos[v][0]], y:[pos[u][1], pos[v][1]],
562
+ line:{color:col, width:wd},
563
+ hoverinfo:'none', showlegend:false
564
+ });
565
+ }
566
+
567
+ // Shared-vertex springs drawn in violet with slight dash to distinguish
568
+ for (const [key, k, u, v] of sharedEdges) {
569
+ const t = Math.min(Math.abs(k) / 6, 1);
570
+ const col = `rgb(${Math.round(160+t*60)},80,${Math.round(200+t*55)})`;
571
+ traces.push({
572
+ type:'scatter', mode:'lines',
573
+ x:[pos[u][0], pos[v][0]], y:[pos[u][1], pos[v][1]],
574
+ line:{color:col, width:1.8+t*2, dash:'dot'},
575
  hoverinfo:'none', showlegend:false
576
  });
577
  }
 
585
  };
586
  const isIO = id => 'ABC'.includes(id[0]);
587
 
588
+ // Mark shared vertices (appear in springs from multiple dims)
589
+ const sharedNodes = new Set();
590
+ for (const key of Object.keys(springs)) {
591
+ if (isSharedSpring(key)) {
592
+ const [u, v] = key.split('β†’');
593
+ if ('UL'.includes((u||'')[0])) sharedNodes.add(u);
594
+ if ('UL'.includes((v||'')[0])) sharedNodes.add(v);
595
+ }
596
+ }
597
+
598
  traces.push({
599
  type:'scatter', mode:'markers+text',
600
  x: allN.map(id => pos[id]?.[0] ?? 0),
 
608
  marker:{
609
  size: allN.map(id => {
610
  const v = Math.abs(nodes[id]?.vel ?? 0);
611
+ const base = isIO(id) ? 18 : (sharedNodes.has(id) ? 14 : 10);
612
+ return base + Math.min(v*30, 8);
613
  }),
614
+ color: allN.map(id => sharedNodes.has(id) ? '#a78bfa' : NCOL(id)),
615
  opacity: allN.map(id => 0.75 + Math.min(Math.abs(nodes[id]?.vel??0)*1.8, 0.25)),
616
  line:{
617
+ width: allN.map(id => sharedNodes.has(id) ? 3.5 : 2.5),
618
+ color: allN.map(id =>
619
+ sharedNodes.has(id) ? '#7c3aed'
620
+ : nodes[id]?.anchored ? '#ef4444' : '#22c55e'
621
+ )
622
  }
623
  },
624
  hoverinfo:'none', showlegend:false
 
658
 
659
  syncTopoUI(d.n_inputs, d.n_upper, d.n_lower);
660
  crossConnect = d.cross_connect;
661
+ nShared = d.n_shared || 0;
662
  updateCrossUI();
 
663
  document.getElementById('b-alpha').innerText = `Ξ±:${d.back_alpha.toFixed(2)}`;
664
  document.getElementById('b-data').innerText = (d.dataset_type||'').slice(0,6).toUpperCase();
665
 
 
704
  }
705
  document.getElementById('pane-nodes').innerHTML = nh;
706
 
707
+ // Springs pane β€” shared-vertex springs shown with violet marker
708
  let sh = '';
709
  for (const [key, k] of Object.entries(d.springs)) {
710
+ const shared = isSharedSpring(key);
711
+ const kc = shared ? 'text-violet-300'
712
+ : (k < 0 ? 'text-blue-300' : k > 4 ? 'text-yellow-200' : 'text-purple-300');
713
+ const pfx = shared ? '<span class="text-violet-500 mr-1">βŠ•</span>' : '';
714
  sh += `<div class="flex justify-between py-0.5 border-b border-slate-900">
715
+ <span>${pfx}<span class="text-slate-500 text-[9px]">${key}</span></span>
716
  <span class="${kc} font-bold text-[10px]">${k.toFixed(4)}</span></div>`;
717
  }
718
  document.getElementById('pane-springs').innerHTML = sh;