Spaces:
Running
Running
2026-04-22T02:58:20Z — viewer + assets
Browse files- .gitattributes +1 -0
- README.md +30 -8
- deploy.sh +36 -0
- index.html +1419 -0
- search-indexes/google-gemma-4-e2b-it.bge.f16.bin +3 -0
- search-indexes/google-gemma-4-e2b-it.json +0 -0
- search-indexes/google-gemma-4-e2b-it.meta.json +7 -0
- vindex-hero-bg.gif +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
vindex-hero-bg.gif filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -1,12 +1,34 @@
|
|
| 1 |
---
|
| 2 |
-
title: Vindex Viewer
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: blue
|
| 6 |
-
sdk:
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: LarQL Vindex Viewer
|
| 3 |
+
emoji: 🔬
|
| 4 |
+
colorFrom: purple
|
| 5 |
colorTo: blue
|
| 6 |
+
sdk: static
|
| 7 |
+
pinned: true
|
| 8 |
+
license: cc-by-nc-4.0
|
| 9 |
+
short_description: Interactive viewer for LarQL transformer vindexes
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# LarQL Vindex Viewer
|
| 13 |
+
|
| 14 |
+
Interactive visualization of the SVD-decomposed feature structure of nine open-weight transformers, built from the [Divinci-AI vindex collection](https://huggingface.co/Divinci-AI).
|
| 15 |
+
|
| 16 |
+
**Two views, both live:**
|
| 17 |
+
- **3D cylinder** (default) — each model is a column of layer rings; feature points spiral around each ring colored by circuit stage (broadcast → domain → entity → prediction).
|
| 18 |
+
- **2D circuit** (`🔌 2D Circuit` button or `?view=flat`) — layers as horizontal rows with inter-layer edges between top features, network-style.
|
| 19 |
+
|
| 20 |
+
**Compare mode** (`⇌ Compare`) renders any two models side-by-side. The default pair (Qwen3-8B vs Bonsai b1.58 1-bit) is the headline visual: organized rings vs scattered dissolution cloud, the same data that drives Paper 1's 1-bit dissolution claim (var@64 ≈ 0.85 for fp16 vs ≈ 0.10 for 1-bit).
|
| 21 |
+
|
| 22 |
+
**Entity search** (`🔍` toolbar input or `?q=paris`) — type a token, see which features it activates across the model's depth. The Paris→capital match on Gemma 4 E2B is the LarQL Gate-3 result that Post 2 in our blog series unpacks.
|
| 23 |
+
|
| 24 |
+
**Demo** (`▶ Demo`) — 12-second scripted tour: build → orbit → reveal compare mode → tagline.
|
| 25 |
+
|
| 26 |
+
URL params: `?model=`, `?view=flat`, `?autoplay`, `?q=`. Combine freely.
|
| 27 |
+
|
| 28 |
+
---
|
| 29 |
+
|
| 30 |
+
## Background
|
| 31 |
+
|
| 32 |
+
A **vindex** is a transformer's weights decompiled into a queryable feature database. The viewer renders the top-64 SVD feature directions per layer — the directions that absorb ~85% of the down-projection matrix's variance in fp16 models and ~10% (near-Marchenko-Pastur random) in 1-bit models.
|
| 33 |
+
|
| 34 |
+
Full details, papers, and the LarQL toolchain: **[huggingface.co/Divinci-AI](https://huggingface.co/Divinci-AI)** · [github.com/Divinci-AI](https://github.com/Divinci-AI)
|
deploy.sh
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Deploy the LarQL Vindex Viewer to HuggingFace Spaces (Divinci-AI/vindex-viewer)
|
| 3 |
+
#
|
| 4 |
+
# First-time setup:
|
| 5 |
+
# pip install -U huggingface_hub
|
| 6 |
+
# hf auth login # paste your write token from https://huggingface.co/settings/tokens
|
| 7 |
+
#
|
| 8 |
+
# Usage:
|
| 9 |
+
# ./deploy.sh # incremental upload of changed files (creates space if missing)
|
| 10 |
+
# ./deploy.sh --create-only # just create the space, don't upload
|
| 11 |
+
#
|
| 12 |
+
# After deploy, the viewer is live at:
|
| 13 |
+
# https://huggingface.co/spaces/Divinci-AI/vindex-viewer
|
| 14 |
+
|
| 15 |
+
set -euo pipefail
|
| 16 |
+
|
| 17 |
+
SPACE_ID="Divinci-AI/vindex-viewer"
|
| 18 |
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
| 19 |
+
|
| 20 |
+
# Try to create the Space (idempotent — succeeds even if it already exists)
|
| 21 |
+
echo "Ensuring Space $SPACE_ID exists..."
|
| 22 |
+
hf repo create "$SPACE_ID" --repo-type space --space-sdk static --exist-ok 2>&1 | grep -v "already created" || true
|
| 23 |
+
|
| 24 |
+
if [[ "${1:-}" == "--create-only" ]]; then
|
| 25 |
+
echo "Created (or already exists). Exiting before upload."
|
| 26 |
+
exit 0
|
| 27 |
+
fi
|
| 28 |
+
|
| 29 |
+
echo ""
|
| 30 |
+
echo "Uploading $HERE/ to $SPACE_ID ..."
|
| 31 |
+
hf upload "$SPACE_ID" "$HERE" . --repo-type space \
|
| 32 |
+
--commit-message "$(date -u +%Y-%m-%dT%H:%M:%SZ) — viewer + assets"
|
| 33 |
+
|
| 34 |
+
echo ""
|
| 35 |
+
echo "✓ Deployed. Live at: https://huggingface.co/spaces/$SPACE_ID"
|
| 36 |
+
echo "Hero GIF URL (used by org card): https://huggingface.co/spaces/$SPACE_ID/resolve/main/vindex-hero-bg.gif"
|
index.html
ADDED
|
@@ -0,0 +1,1419 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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>LarQL Vindex Viewer — Divinci AI</title>
|
| 7 |
+
<style>
|
| 8 |
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
| 9 |
+
body { background: #050510; color: #e0e8ff; font-family: 'Courier New', monospace; overflow: hidden; }
|
| 10 |
+
|
| 11 |
+
canvas { display: block; }
|
| 12 |
+
|
| 13 |
+
/* ── Top UI bar ─────────────────────────────────────────────── */
|
| 14 |
+
#ui {
|
| 15 |
+
position: absolute; top: 0; left: 0; right: 0;
|
| 16 |
+
padding: 12px 16px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
|
| 17 |
+
background: linear-gradient(to bottom, rgba(5,5,16,0.95), transparent);
|
| 18 |
+
pointer-events: none; z-index: 10;
|
| 19 |
+
}
|
| 20 |
+
#ui > * { pointer-events: all; }
|
| 21 |
+
.logo { font-size: 11px; opacity: 0.5; letter-spacing: 0.12em; white-space: nowrap; }
|
| 22 |
+
.logo span { color: #7b9fff; }
|
| 23 |
+
|
| 24 |
+
select, button {
|
| 25 |
+
background: rgba(20,24,60,0.9); border: 1px solid rgba(123,159,255,0.28);
|
| 26 |
+
color: #e0e8ff; padding: 5px 10px; border-radius: 5px; font-size: 11px;
|
| 27 |
+
cursor: pointer; outline: none; font-family: inherit; transition: border-color 0.15s;
|
| 28 |
+
}
|
| 29 |
+
select:hover, button:hover { border-color: rgba(123,159,255,0.65); }
|
| 30 |
+
button.active { border-color: #7b9fff; background: rgba(40,55,130,0.9); color: #fff; }
|
| 31 |
+
#hint { font-size: 10px; opacity: 0.35; }
|
| 32 |
+
#search-input {
|
| 33 |
+
background: rgba(20,24,60,0.9); border: 1px solid rgba(123,159,255,0.28);
|
| 34 |
+
color: #e0e8ff; padding: 5px 10px; border-radius: 5px; font-size: 11px;
|
| 35 |
+
width: 250px; font-family: inherit; outline: none; transition: border-color 0.15s;
|
| 36 |
+
}
|
| 37 |
+
#search-input:focus { border-color: #7b9fff; }
|
| 38 |
+
#search-input::placeholder { color: rgba(224,232,255,0.3); }
|
| 39 |
+
#search-status { font-size: 10px; opacity: 0.55; color: #ffd060; min-width: 110px; }
|
| 40 |
+
|
| 41 |
+
/* ── Tier 1 info panel — always visible ─────────────────────── */
|
| 42 |
+
#info-panel {
|
| 43 |
+
position: absolute; bottom: 16px; left: 16px;
|
| 44 |
+
background: rgba(6,7,20,0.93); border: 1px solid rgba(123,159,255,0.2);
|
| 45 |
+
border-radius: 10px; min-width: 240px; max-width: 280px;
|
| 46 |
+
font-size: 11px; z-index: 10; overflow: hidden;
|
| 47 |
+
box-shadow: 0 4px 24px rgba(0,0,0,0.5);
|
| 48 |
+
}
|
| 49 |
+
.panel-tier1 { padding: 12px 14px 10px; }
|
| 50 |
+
.model-title {
|
| 51 |
+
font-size: 13px; font-weight: bold; letter-spacing: 0.04em;
|
| 52 |
+
color: #e8f0ff; display: flex; justify-content: space-between; align-items: flex-start;
|
| 53 |
+
gap: 8px; margin-bottom: 3px;
|
| 54 |
+
}
|
| 55 |
+
.hf-link {
|
| 56 |
+
font-size: 9px; color: #7b9fff; text-decoration: none; opacity: 0.8;
|
| 57 |
+
border: 1px solid rgba(123,159,255,0.25); border-radius: 3px; padding: 1px 5px;
|
| 58 |
+
white-space: nowrap; flex-shrink: 0;
|
| 59 |
+
}
|
| 60 |
+
.hf-link:hover { opacity: 1; border-color: rgba(123,159,255,0.6); }
|
| 61 |
+
.model-meta { font-size: 10px; opacity: 0.45; margin-bottom: 10px; letter-spacing: 0.02em; }
|
| 62 |
+
|
| 63 |
+
.tier1-divider { border: none; border-top: 1px solid rgba(123,159,255,0.1); margin: 8px 0; }
|
| 64 |
+
|
| 65 |
+
.c4-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
| 66 |
+
.c4-val { font-size: 15px; font-weight: bold; font-family: monospace; }
|
| 67 |
+
.c4-val.good { color: #44ffaa; }
|
| 68 |
+
.c4-val.warm { color: #ffcc44; }
|
| 69 |
+
.c4-val.hot { color: #ff6644; }
|
| 70 |
+
.c4-label { font-size: 9px; opacity: 0.5; }
|
| 71 |
+
.c4-chip {
|
| 72 |
+
margin-left: auto; font-size: 9px; padding: 2px 6px; border-radius: 10px;
|
| 73 |
+
font-weight: bold; letter-spacing: 0.04em;
|
| 74 |
+
}
|
| 75 |
+
.chip-good { background: rgba(68,255,170,0.15); color: #44ffaa; border: 1px solid rgba(68,255,170,0.3); }
|
| 76 |
+
.chip-warn { background: rgba(255,180,50,0.15); color: #ffcc44; border: 1px solid rgba(255,180,50,0.3); }
|
| 77 |
+
.chip-alert { background: rgba(255,80,50,0.15); color: #ff6644; border: 1px solid rgba(255,80,50,0.3); }
|
| 78 |
+
.chip-dead { background: rgba(255,40,40,0.18); color: #ff4444; border: 1px solid rgba(255,40,40,0.3); }
|
| 79 |
+
|
| 80 |
+
.stage-bar-row { display: flex; align-items: center; gap: 7px; margin-bottom: 4px; }
|
| 81 |
+
.stage-bar-label { font-size: 9px; opacity: 0.45; width: 60px; flex-shrink: 0; }
|
| 82 |
+
.stage-bar-track { flex: 1; height: 5px; background: rgba(255,255,255,0.07); border-radius: 2px; overflow: hidden; }
|
| 83 |
+
.stage-bar-fill { height: 100%; border-radius: 2px; transition: width 0.4s ease; }
|
| 84 |
+
.stage-bar-val { font-size: 9px; width: 28px; text-align: right; opacity: 0.7; }
|
| 85 |
+
|
| 86 |
+
.gate3-row {
|
| 87 |
+
display: flex; align-items: center; gap: 6px;
|
| 88 |
+
margin-top: 7px; font-size: 10px;
|
| 89 |
+
}
|
| 90 |
+
.gate3-icon { font-size: 11px; }
|
| 91 |
+
|
| 92 |
+
.anomaly-banner {
|
| 93 |
+
margin: 8px 14px; padding: 6px 10px;
|
| 94 |
+
background: rgba(255,100,50,0.1); border: 1px solid rgba(255,100,50,0.25);
|
| 95 |
+
border-radius: 6px; font-size: 10px; color: #ffaa66; cursor: pointer;
|
| 96 |
+
display: none; line-height: 1.5;
|
| 97 |
+
}
|
| 98 |
+
.anomaly-banner:hover { background: rgba(255,100,50,0.18); }
|
| 99 |
+
|
| 100 |
+
/* ── Tier 2 — expandable metrics ───────────────────────────── */
|
| 101 |
+
.tier2-toggle {
|
| 102 |
+
width: 100%; text-align: center; padding: 6px; font-size: 9px;
|
| 103 |
+
letter-spacing: 0.1em; opacity: 0.4; cursor: pointer; border: none;
|
| 104 |
+
background: rgba(123,159,255,0.04); border-top: 1px solid rgba(123,159,255,0.1);
|
| 105 |
+
color: #e0e8ff; text-transform: uppercase; transition: opacity 0.15s, background 0.15s;
|
| 106 |
+
}
|
| 107 |
+
.tier2-toggle:hover { opacity: 0.75; background: rgba(123,159,255,0.08); }
|
| 108 |
+
|
| 109 |
+
#tier2-metrics { display: none; padding: 10px 14px 12px; border-top: 1px solid rgba(123,159,255,0.08); }
|
| 110 |
+
#tier2-metrics.open { display: block; }
|
| 111 |
+
|
| 112 |
+
.m-row { display: flex; align-items: center; gap: 6px; margin-bottom: 7px; }
|
| 113 |
+
.m-name { font-size: 9px; opacity: 0.45; width: 64px; flex-shrink: 0; letter-spacing: 0.02em; }
|
| 114 |
+
.m-bar-track { flex: 1; height: 4px; background: rgba(255,255,255,0.07); border-radius: 2px; overflow: hidden; }
|
| 115 |
+
.m-bar-fill { height: 100%; border-radius: 2px; }
|
| 116 |
+
.m-val { font-size: 9px; width: 36px; text-align: right; font-family: monospace; }
|
| 117 |
+
.m-val.good { color: #44ffaa; }
|
| 118 |
+
.m-val.warn { color: #ffcc44; }
|
| 119 |
+
.m-val.alert { color: #ff6644; }
|
| 120 |
+
.m-val.dim { opacity: 0.35; }
|
| 121 |
+
|
| 122 |
+
.shortid-row {
|
| 123 |
+
display: flex; align-items: center; justify-content: space-between;
|
| 124 |
+
margin-top: 6px; padding-top: 6px; border-top: 1px solid rgba(123,159,255,0.08);
|
| 125 |
+
font-size: 9px;
|
| 126 |
+
}
|
| 127 |
+
.shortid-val { font-family: monospace; opacity: 0.45; }
|
| 128 |
+
.copy-btn {
|
| 129 |
+
font-size: 9px; padding: 1px 6px; border-radius: 3px; cursor: pointer;
|
| 130 |
+
border: 1px solid rgba(123,159,255,0.2); background: transparent; color: #7b9fff;
|
| 131 |
+
}
|
| 132 |
+
.copy-btn:hover { background: rgba(123,159,255,0.1); }
|
| 133 |
+
|
| 134 |
+
/* ── Legend ──────────────────────────────────────────────────── */
|
| 135 |
+
#legend {
|
| 136 |
+
position: absolute; bottom: 16px; right: 16px;
|
| 137 |
+
background: rgba(6,7,20,0.93); border: 1px solid rgba(123,159,255,0.2);
|
| 138 |
+
border-radius: 10px; padding: 12px 15px; font-size: 11px; line-height: 1.9; z-index: 10;
|
| 139 |
+
}
|
| 140 |
+
#legend h3 { font-size: 9px; letter-spacing: 0.14em; color: #7b9fff; margin-bottom: 6px; text-transform: uppercase; }
|
| 141 |
+
.stage-row { display: flex; align-items: center; gap: 7px; }
|
| 142 |
+
.stage-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
| 143 |
+
|
| 144 |
+
/* ── Compare panel ───────────────────────────────────────────── */
|
| 145 |
+
#compare-panel {
|
| 146 |
+
position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);
|
| 147 |
+
background: rgba(6,7,20,0.93); border: 1px solid rgba(123,159,255,0.2);
|
| 148 |
+
border-radius: 10px; padding: 12px 16px; font-size: 11px;
|
| 149 |
+
display: none; z-index: 10; white-space: nowrap;
|
| 150 |
+
box-shadow: 0 4px 24px rgba(0,0,0,0.5);
|
| 151 |
+
}
|
| 152 |
+
#compare-panel h3 { font-size: 9px; letter-spacing: 0.14em; color: #7b9fff; margin-bottom: 8px; text-transform: uppercase; }
|
| 153 |
+
.cmp-row { display: flex; gap: 28px; }
|
| 154 |
+
.cmp-col { display: flex; flex-direction: column; gap: 4px; min-width: 140px; }
|
| 155 |
+
.cmp-title { font-size: 10px; font-weight: bold; color: #e8f0ff; margin-bottom: 2px; }
|
| 156 |
+
.cmp-meta { font-size: 9px; opacity: 0.4; margin-bottom: 4px; }
|
| 157 |
+
.cmp-metric { display: flex; justify-content: space-between; gap: 14px; font-size: 10px; }
|
| 158 |
+
.cmp-val { font-family: monospace; }
|
| 159 |
+
.cmp-val.good { color: #44ffaa; }
|
| 160 |
+
.cmp-val.warn { color: #ffcc44; }
|
| 161 |
+
.cmp-val.alert { color: #ff6644; }
|
| 162 |
+
.cmp-divider { width: 1px; background: rgba(123,159,255,0.15); }
|
| 163 |
+
|
| 164 |
+
/* ── Tooltip ─────────────────────────────────────────────────── */
|
| 165 |
+
#tooltip {
|
| 166 |
+
position: absolute; display: none; pointer-events: none;
|
| 167 |
+
background: rgba(8,10,32,0.97); border: 1px solid rgba(123,159,255,0.35);
|
| 168 |
+
border-radius: 7px; padding: 8px 12px; font-size: 10px; line-height: 1.7;
|
| 169 |
+
color: #c8d8ff; z-index: 20; max-width: 220px;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
/* ── Demo overlay ────────────────────────────────────────────── */
|
| 173 |
+
#demo-overlay {
|
| 174 |
+
position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
|
| 175 |
+
font-size: 13px; letter-spacing: 0.08em; color: rgba(255,255,255,0.7);
|
| 176 |
+
text-align: center; z-index: 30; display: none;
|
| 177 |
+
text-shadow: 0 2px 12px rgba(0,0,0,0.8);
|
| 178 |
+
pointer-events: none;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
#loading {
|
| 182 |
+
position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
|
| 183 |
+
text-align: center; color: #7b9fff; z-index: 30; display: none;
|
| 184 |
+
}
|
| 185 |
+
.spinner { width: 32px; height: 32px; border: 2px solid rgba(123,159,255,0.15); border-top-color: #7b9fff; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 10px; }
|
| 186 |
+
@keyframes spin { to { transform: rotate(360deg); } }
|
| 187 |
+
</style>
|
| 188 |
+
</head>
|
| 189 |
+
<body>
|
| 190 |
+
|
| 191 |
+
<!-- Top bar -->
|
| 192 |
+
<div id="ui">
|
| 193 |
+
<div class="logo"><span>Divinci AI</span> · LarQL Vindex Viewer v2</div>
|
| 194 |
+
<select id="model-select">
|
| 195 |
+
<option value="gemma-4-e2b">Gemma 4 E2B (2B) — 35L ✓</option>
|
| 196 |
+
<option value="ministral-3b">Ministral-3B — 26L</option>
|
| 197 |
+
<option value="medgemma-4b">MedGemma-1.5-4B — 34L ⚠️</option>
|
| 198 |
+
<option value="qwen3-0.6b">Qwen3-0.6B — 28L</option>
|
| 199 |
+
<option value="qwen3-8b" selected>Qwen3-8B — 36L</option>
|
| 200 |
+
<option value="qwen3.6-35b">Qwen3.6-35B MoE — 40L</option>
|
| 201 |
+
<option value="llama-3.1-8b">Llama-3.1-8B — 32L</option>
|
| 202 |
+
<option value="gpt-oss-120b">GPT-OSS-120B MoE — 36L</option>
|
| 203 |
+
<option value="bonsai-1bit">Bonsai b1.58-2B 1-bit 💀</option>
|
| 204 |
+
</select>
|
| 205 |
+
<button id="compare-btn" title="Compare current model vs Bonsai 1-bit dissolution">⇌ Compare</button>
|
| 206 |
+
<button id="demo-btn" title="12-second scripted tour">▶ Demo</button>
|
| 207 |
+
<button id="view-btn" title="Toggle between 3D cylinder and 2D circuit view">🔌 2D Circuit</button>
|
| 208 |
+
<input id="search-input" type="search" placeholder="🔍 Search features (e.g. paris, capital, code)…" autocomplete="off" />
|
| 209 |
+
<span id="search-status"></span>
|
| 210 |
+
<div id="hint">Click a feature · drag to orbit · scroll to zoom</div>
|
| 211 |
+
</div>
|
| 212 |
+
|
| 213 |
+
<!-- Tier 1 info panel -->
|
| 214 |
+
<div id="info-panel">
|
| 215 |
+
<div class="panel-tier1">
|
| 216 |
+
<div class="model-title">
|
| 217 |
+
<span id="m-name">—</span>
|
| 218 |
+
<a id="m-hf-link" class="hf-link" href="#" target="_blank" rel="noopener">↗ HF</a>
|
| 219 |
+
</div>
|
| 220 |
+
<div class="model-meta" id="m-meta">—</div>
|
| 221 |
+
<hr class="tier1-divider">
|
| 222 |
+
<!-- C4 row -->
|
| 223 |
+
<div class="c4-row">
|
| 224 |
+
<div>
|
| 225 |
+
<div class="c4-val" id="m-c4-val">—</div>
|
| 226 |
+
<div class="c4-label">C4 TEMPERATURE</div>
|
| 227 |
+
</div>
|
| 228 |
+
<div class="c4-chip" id="m-c4-chip">—</div>
|
| 229 |
+
</div>
|
| 230 |
+
<!-- Stage bar -->
|
| 231 |
+
<div class="stage-bar-row">
|
| 232 |
+
<div class="stage-bar-label">CIRCUIT</div>
|
| 233 |
+
<div class="stage-bar-track"><div class="stage-bar-fill" id="m-stage-fill" style="width:0%"></div></div>
|
| 234 |
+
<div class="stage-bar-val" id="m-c5-val">—</div>
|
| 235 |
+
</div>
|
| 236 |
+
<!-- Gate 3 -->
|
| 237 |
+
<div class="gate3-row">
|
| 238 |
+
<span class="gate3-icon" id="m-gate3-icon">○</span>
|
| 239 |
+
<span id="m-gate3-label" style="opacity:0.5">Gate-3: —</span>
|
| 240 |
+
</div>
|
| 241 |
+
</div>
|
| 242 |
+
<!-- Anomaly banner -->
|
| 243 |
+
<div class="anomaly-banner" id="m-anomaly"></div>
|
| 244 |
+
<!-- Tier 2 toggle -->
|
| 245 |
+
<button class="tier2-toggle" id="tier2-btn">METRICS ▾</button>
|
| 246 |
+
<!-- Tier 2 body -->
|
| 247 |
+
<div id="tier2-metrics">
|
| 248 |
+
<div class="m-row">
|
| 249 |
+
<div class="m-name">C1 SPARSITY</div>
|
| 250 |
+
<div class="m-bar-track"><div class="m-bar-fill" id="b-c1" style="width:0%;background:#44aaff"></div></div>
|
| 251 |
+
<div class="m-val" id="v-c1">—</div>
|
| 252 |
+
</div>
|
| 253 |
+
<div class="m-row">
|
| 254 |
+
<div class="m-name">C2 TOP-8</div>
|
| 255 |
+
<div class="m-bar-track"><div class="m-bar-fill" id="b-c2" style="width:0%;background:#44ffaa"></div></div>
|
| 256 |
+
<div class="m-val" id="v-c2">—</div>
|
| 257 |
+
</div>
|
| 258 |
+
<div class="m-row">
|
| 259 |
+
<div class="m-name">C3 COHERENCE</div>
|
| 260 |
+
<div class="m-bar-track"><div class="m-bar-fill" id="b-c3" style="width:0%;background:#7b9fff"></div></div>
|
| 261 |
+
<div class="m-val" id="v-c3">—</div>
|
| 262 |
+
</div>
|
| 263 |
+
<div class="m-row">
|
| 264 |
+
<div class="m-name">C4 TEMP</div>
|
| 265 |
+
<div class="m-bar-track"><div class="m-bar-fill" id="b-c4" style="width:0%;background:#ffaa44"></div></div>
|
| 266 |
+
<div class="m-val" id="v-c4">—</div>
|
| 267 |
+
</div>
|
| 268 |
+
<div class="m-row">
|
| 269 |
+
<div class="m-name">SPECTRUM</div>
|
| 270 |
+
<div class="m-bar-track"><div class="m-bar-fill" id="b-spec" style="width:0%;background:#a855f7"></div></div>
|
| 271 |
+
<div class="m-val" id="v-spec">—</div>
|
| 272 |
+
</div>
|
| 273 |
+
<div class="shortid-row">
|
| 274 |
+
<div>
|
| 275 |
+
<span style="opacity:0.35;font-size:9px">VINDEX ID </span>
|
| 276 |
+
<span class="shortid-val" id="v-shortid">—</span>
|
| 277 |
+
</div>
|
| 278 |
+
<button class="copy-btn" id="copy-shortid">copy</button>
|
| 279 |
+
</div>
|
| 280 |
+
</div>
|
| 281 |
+
</div>
|
| 282 |
+
|
| 283 |
+
<!-- Legend -->
|
| 284 |
+
<div id="legend">
|
| 285 |
+
<h3>Circuit Stages</h3>
|
| 286 |
+
<div class="stage-row"><div class="stage-dot" style="background:#4466ff"></div>Broadcast (0%)</div>
|
| 287 |
+
<div class="stage-row"><div class="stage-dot" style="background:#44aaff"></div>Domain (20-25%)</div>
|
| 288 |
+
<div class="stage-row"><div class="stage-dot" style="background:#44ffaa"></div>Entity (45-55%)</div>
|
| 289 |
+
<div class="stage-row"><div class="stage-dot" style="background:#ffaa44"></div>Prediction (90%+)</div>
|
| 290 |
+
<div class="stage-row"><div class="stage-dot" style="background:#888"></div>Transition</div>
|
| 291 |
+
<div class="stage-row" style="margin-top:5px"><div class="stage-dot" style="background:#ff4444"></div>Dissolved (1-bit)</div>
|
| 292 |
+
</div>
|
| 293 |
+
|
| 294 |
+
<!-- Compare panel -->
|
| 295 |
+
<div id="compare-panel">
|
| 296 |
+
<h3>Circuit Comparison</h3>
|
| 297 |
+
<div class="cmp-row">
|
| 298 |
+
<div class="cmp-col" id="cmp-a"></div>
|
| 299 |
+
<div class="cmp-divider"></div>
|
| 300 |
+
<div class="cmp-col" id="cmp-b"></div>
|
| 301 |
+
</div>
|
| 302 |
+
</div>
|
| 303 |
+
|
| 304 |
+
<!-- Demo text overlay -->
|
| 305 |
+
<div id="demo-overlay"></div>
|
| 306 |
+
|
| 307 |
+
<div id="tooltip"></div>
|
| 308 |
+
<div id="loading"><div class="spinner"></div><div>Loading…</div></div>
|
| 309 |
+
|
| 310 |
+
<script type="importmap">
|
| 311 |
+
{
|
| 312 |
+
"imports": {
|
| 313 |
+
"three": "https://unpkg.com/three@0.160/build/three.module.js",
|
| 314 |
+
"three/addons/": "https://unpkg.com/three@0.160/examples/jsm/"
|
| 315 |
+
}
|
| 316 |
+
}
|
| 317 |
+
</script>
|
| 318 |
+
<script type="module">
|
| 319 |
+
import * as THREE from 'three';
|
| 320 |
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
| 321 |
+
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
| 322 |
+
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
| 323 |
+
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
|
| 324 |
+
|
| 325 |
+
// ── Stage colors ───────────────────────────────────────────────
|
| 326 |
+
const STAGE_COLORS = [
|
| 327 |
+
new THREE.Color(0x4466ff), // broadcast
|
| 328 |
+
new THREE.Color(0x44aaff), // domain
|
| 329 |
+
new THREE.Color(0x44ffaa), // entity
|
| 330 |
+
new THREE.Color(0xffaa44), // prediction
|
| 331 |
+
new THREE.Color(0x888888), // transition
|
| 332 |
+
new THREE.Color(0xff4444), // dissolved
|
| 333 |
+
];
|
| 334 |
+
const STAGE_NAMES = ['Broadcast','Domain','Entity','Prediction','Transition','Dissolved'];
|
| 335 |
+
|
| 336 |
+
// ── Real Phase 1+2 measurements (April 2026) ──────────────────
|
| 337 |
+
const VINDEX_DATA = {
|
| 338 |
+
'gemma-4-e2b': {
|
| 339 |
+
name: 'Gemma 4 E2B', org: 'Google', params: '2B', precision: 'bf16',
|
| 340 |
+
layers: 35, F: 128,
|
| 341 |
+
c1: 0.061, c2: 0.938, c3: 0.801, c4: 0.041, c5: 4, c5max: 5,
|
| 342 |
+
spectrum: 'power-law', spectrumScore: 0.92,
|
| 343 |
+
shortId: 'gemma4e2b', hfRepo: 'Divinci-AI/gemma-4-e2b-vindex',
|
| 344 |
+
gate3: 'PASS', gate3note: 'Paris→capital suppressed (18.1→absent)',
|
| 345 |
+
finding: 'Confirms C4≈0.042 universal constant',
|
| 346 |
+
anomaly: null, dissolved: false,
|
| 347 |
+
stageBands: [[0,.04,0],[.20,.25,1],[.45,.55,2],[.90,1,3]],
|
| 348 |
+
},
|
| 349 |
+
'ministral-3b': {
|
| 350 |
+
name: 'Ministral-3B', org: 'Mistral AI', params: '3B', precision: 'fp8→bf16',
|
| 351 |
+
layers: 26, F: 128,
|
| 352 |
+
c1: 0.236, c2: null, c3: 0.724, c4: 0.265, c5: 3, c5max: 5,
|
| 353 |
+
spectrum: 'power-law', spectrumScore: 0.88,
|
| 354 |
+
shortId: '00d28f96', hfRepo: 'Divinci-AI/ministral-3b-vindex',
|
| 355 |
+
gate3: 'PENDING', gate3note: 'Awaiting multi-layer LarQL service',
|
| 356 |
+
finding: 'C4 higher than expected (fp8 dequant on A100 compute 8.0). C2 N/A — multimodal lm_head absent.',
|
| 357 |
+
anomaly: null, dissolved: false,
|
| 358 |
+
stageBands: [[0,.04,0],[.20,.26,1],[.46,.54,2],[.88,1,3]],
|
| 359 |
+
},
|
| 360 |
+
'medgemma-4b': {
|
| 361 |
+
name: 'MedGemma-1.5-4B', org: 'Google', params: '4B', precision: 'bf16',
|
| 362 |
+
layers: 34, F: 128,
|
| 363 |
+
c1: 0.181, c2: 0.795, c3: 0.800, c4: 1.898, c5: 2, c5max: 5,
|
| 364 |
+
spectrum: 'power-law', spectrumScore: 0.78,
|
| 365 |
+
shortId: '75eeb232', hfRepo: 'Divinci-AI/medgemma-1.5-4b-vindex',
|
| 366 |
+
gate3: 'PENDING', gate3note: 'Gate-3 pending',
|
| 367 |
+
finding: 'C4 anomaly: 1.898 — 45× cohort average. Hypothesis: text-only activation probe through multimodal vision encoder inflates temperature measurement.',
|
| 368 |
+
anomaly: '⚠️ C4 = 1.898 — 45× cohort anomaly. Likely artifact of text-only probe through vision encoder. Under investigation.',
|
| 369 |
+
anomalyType: 'warn', dissolved: false,
|
| 370 |
+
stageBands: [[0,.04,0],[.20,.26,1],[.45,.54,2],[.90,1,3]],
|
| 371 |
+
},
|
| 372 |
+
'qwen3-0.6b': {
|
| 373 |
+
name: 'Qwen3-0.6B', org: 'Alibaba Cloud', params: '0.6B', precision: 'bf16',
|
| 374 |
+
layers: 28, F: 128,
|
| 375 |
+
c1: 0.117, c2: 0.880, c3: 0.531, c4: 0.411, c5: 4, c5max: 5,
|
| 376 |
+
spectrum: 'power-law', spectrumScore: 0.83,
|
| 377 |
+
shortId: 'qwen306b', hfRepo: 'Divinci-AI/qwen3-0.6b-vindex',
|
| 378 |
+
gate3: 'PENDING', gate3note: 'Awaiting multi-layer LarQL service',
|
| 379 |
+
finding: 'Qwen3 family C4 elevation (10×). C3 coherence lower than larger Qwen3 models — scale matters for gate alignment.',
|
| 380 |
+
anomaly: null, dissolved: false,
|
| 381 |
+
stageBands: [[0,.04,0],[.21,.25,1],[.46,.54,2],[.90,1,3]],
|
| 382 |
+
},
|
| 383 |
+
'qwen3-8b': {
|
| 384 |
+
name: 'Qwen3-8B', org: 'Alibaba Cloud', params: '8B', precision: 'bf16',
|
| 385 |
+
layers: 36, F: 128,
|
| 386 |
+
c1: 0.228, c2: 0.867, c3: 0.813, c4: 0.804, c5: 4, c5max: 5,
|
| 387 |
+
spectrum: 'power-law', spectrumScore: 0.87,
|
| 388 |
+
shortId: 'ea08e8e8', hfRepo: 'Divinci-AI/qwen3-8b-vindex',
|
| 389 |
+
gate3: 'PENDING', gate3note: 'Awaiting multi-layer LarQL service',
|
| 390 |
+
finding: 'Qwen3 family C4 elevation (19×) confirmed as architectural signature — same in bf16 as in 1-bit Bonsai. Four-stage circuit intact.',
|
| 391 |
+
anomaly: null, dissolved: false,
|
| 392 |
+
stageBands: [[0,.04,0],[.20,.25,1],[.45,.55,2],[.90,1,3]],
|
| 393 |
+
},
|
| 394 |
+
'qwen3.6-35b': {
|
| 395 |
+
name: 'Qwen3.6-35B-A3B', org: 'Alibaba Cloud', params: '35B (3B active)', precision: 'bf16',
|
| 396 |
+
layers: 40, F: 64,
|
| 397 |
+
c1: null, c2: null, c3: null, c4: null, c5: null, c5max: 5,
|
| 398 |
+
spectrum: 'var@64: 0.27–0.39', spectrumScore: 0.55,
|
| 399 |
+
shortId: 'qwen3635b', hfRepo: 'Divinci-AI/qwen3.6-35b-a3b-vindex',
|
| 400 |
+
gate3: 'N/A', gate3note: 'Phase 2 pending — MoE 256 experts',
|
| 401 |
+
finding: 'Phase 1 complete. Phase 2 skipped — OOM on A100 with 256-expert MoE routing.',
|
| 402 |
+
anomaly: null, dissolved: false,
|
| 403 |
+
stageBands: [[0,.04,0],[.20,.26,1],[.46,.54,2],[.90,1,3]],
|
| 404 |
+
},
|
| 405 |
+
'llama-3.1-8b': {
|
| 406 |
+
name: 'Llama 3.1-8B', org: 'Meta', params: '8B', precision: 'bf16',
|
| 407 |
+
layers: 32, F: 128,
|
| 408 |
+
c1: 0.387, c2: 0.491, c3: 0.808, c4: 0.012, c5: 2, c5max: 5,
|
| 409 |
+
spectrum: 'power-law', spectrumScore: 0.86,
|
| 410 |
+
shortId: 'c39fad08', hfRepo: 'Divinci-AI/llama-3.1-8b-vindex',
|
| 411 |
+
gate3: 'PENDING', gate3note: 'Awaiting multi-layer LarQL service',
|
| 412 |
+
finding: 'C4 = 0.012 — Llama family runs below the universal constant. C1 sparsity (0.387) highest of all measured models. Base model: C2 reflects unconditional next-token distribution.',
|
| 413 |
+
anomaly: null, dissolved: false,
|
| 414 |
+
stageBands: [[0,.04,0],[.22,.26,1],[.47,.55,2],[.90,1,3]],
|
| 415 |
+
},
|
| 416 |
+
'gpt-oss-120b': {
|
| 417 |
+
name: 'GPT-OSS-120B', org: 'OpenAI', params: '120B', precision: 'MXFP4',
|
| 418 |
+
layers: 36, F: 128,
|
| 419 |
+
c1: null, c2: null, c3: null, c4: null, c5: null, c5max: 5,
|
| 420 |
+
spectrum: 'S[0]=13,056 (117×)', spectrumScore: 0.72,
|
| 421 |
+
shortId: '02d09974', hfRepo: 'Divinci-AI/gpt-oss-120b-vindex',
|
| 422 |
+
gate3: 'N/A', gate3note: 'Uniform MoE routing — calibration required',
|
| 423 |
+
finding: 'Phase 2 skipped (OOM). Dominant SV = 13,056 — 117× the 200th SV. Most extreme power-law spectrum in the cohort. MXFP4 quantization preserves structural hierarchy.',
|
| 424 |
+
anomaly: null, dissolved: false,
|
| 425 |
+
stageBands: [[0,.04,0],[.20,.25,1],[.45,.55,2],[.90,1,3]],
|
| 426 |
+
},
|
| 427 |
+
'bonsai-1bit': {
|
| 428 |
+
name: 'Bonsai b1.58-2B', org: 'Microsoft', params: '2B', precision: '1-bit {-1,0,+1}',
|
| 429 |
+
layers: 28, F: 128,
|
| 430 |
+
c1: 0.223, c2: 0.375, c3: 0.534, c4: 0.429, c5: 1, c5max: 5,
|
| 431 |
+
spectrum: 'flat (var@128=9.3%)', spectrumScore: 0.09,
|
| 432 |
+
shortId: 'bonsai1b', hfRepo: null,
|
| 433 |
+
gate3: 'N/A', gate3note: 'Ternary weights — Phase 1 redo pending',
|
| 434 |
+
finding: 'C5 = 1: four-stage circuit completely dissolved. SV spectrum near-random (Marchenko-Pastur). C4 = 0.429 is Qwen3 architectural signature, not 1-bit effect. The 1-bit discriminators are C5 collapse + flat SV spectrum only.',
|
| 435 |
+
anomaly: '💀 C5 = 1 — four-stage circuit dissolved. Weight SV spectrum is near-random. The model answers correctly, but its internals have no discernible structure.',
|
| 436 |
+
anomalyType: 'dead', dissolved: true,
|
| 437 |
+
stageBands: [],
|
| 438 |
+
},
|
| 439 |
+
};
|
| 440 |
+
|
| 441 |
+
// ── Renderer ──────────────────────────────────────────────────
|
| 442 |
+
const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
|
| 443 |
+
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
|
| 444 |
+
renderer.setSize(innerWidth, innerHeight);
|
| 445 |
+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
| 446 |
+
renderer.toneMappingExposure = 1.15;
|
| 447 |
+
document.body.appendChild(renderer.domElement);
|
| 448 |
+
|
| 449 |
+
const scene = new THREE.Scene();
|
| 450 |
+
scene.background = new THREE.Color(0x050510);
|
| 451 |
+
scene.fog = new THREE.FogExp2(0x050510, 0.012);
|
| 452 |
+
|
| 453 |
+
const camera = new THREE.PerspectiveCamera(52, innerWidth / innerHeight, 0.1, 600);
|
| 454 |
+
camera.position.set(0, 4, 26);
|
| 455 |
+
|
| 456 |
+
const controls = new OrbitControls(camera, renderer.domElement);
|
| 457 |
+
controls.enableDamping = true;
|
| 458 |
+
controls.dampingFactor = 0.06;
|
| 459 |
+
controls.minDistance = 7;
|
| 460 |
+
controls.maxDistance = 120;
|
| 461 |
+
|
| 462 |
+
scene.add(new THREE.AmbientLight(0x334466, 2.8));
|
| 463 |
+
const dirLight = new THREE.DirectionalLight(0x7799ff, 1.6);
|
| 464 |
+
dirLight.position.set(10, 20, 10);
|
| 465 |
+
scene.add(dirLight);
|
| 466 |
+
|
| 467 |
+
const composer = new EffectComposer(renderer);
|
| 468 |
+
composer.addPass(new RenderPass(scene, camera));
|
| 469 |
+
const bloom = new UnrealBloomPass(new THREE.Vector2(innerWidth, innerHeight), 0.7, 0.4, 0.12);
|
| 470 |
+
composer.addPass(bloom);
|
| 471 |
+
|
| 472 |
+
// ── State ─────────────────────────────────────────────────────
|
| 473 |
+
let groups = {};
|
| 474 |
+
let activeKeys = [];
|
| 475 |
+
let walkLines = [];
|
| 476 |
+
let selectedIdx = -1;
|
| 477 |
+
let compareMode = false;
|
| 478 |
+
let demoRunning = false;
|
| 479 |
+
let viewMode = 'cylinder'; // 'cylinder' (3D) or 'flat' (2D circuit)
|
| 480 |
+
let tier2Open = false;
|
| 481 |
+
|
| 482 |
+
// ── PRNG ──────────────────────────────────────────────────────
|
| 483 |
+
function mulberry32(seed) {
|
| 484 |
+
return () => {
|
| 485 |
+
seed |= 0; seed = seed + 0x6D2B79F5 | 0;
|
| 486 |
+
let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
|
| 487 |
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
| 488 |
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
| 489 |
+
};
|
| 490 |
+
}
|
| 491 |
+
|
| 492 |
+
function getStageForDepth(stageBands, depth) {
|
| 493 |
+
for (const [d0, d1, s] of stageBands) {
|
| 494 |
+
if (depth >= d0 && depth <= d1) return s;
|
| 495 |
+
}
|
| 496 |
+
return 4;
|
| 497 |
+
}
|
| 498 |
+
|
| 499 |
+
// ── Build model group ─────────────────────────────────────────
|
| 500 |
+
function buildGroup(key, offsetX = 0) {
|
| 501 |
+
if (groups[key]) {
|
| 502 |
+
scene.remove(groups[key].group);
|
| 503 |
+
groups[key].group.traverse(o => {
|
| 504 |
+
if (o.geometry) o.geometry.dispose();
|
| 505 |
+
if (o.material) {
|
| 506 |
+
if (Array.isArray(o.material)) o.material.forEach(m => m.dispose());
|
| 507 |
+
else o.material.dispose();
|
| 508 |
+
}
|
| 509 |
+
});
|
| 510 |
+
delete groups[key];
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
const d = VINDEX_DATA[key];
|
| 514 |
+
const group = new THREE.Group();
|
| 515 |
+
group.position.x = offsetX;
|
| 516 |
+
|
| 517 |
+
const L = d.layers, F = d.F, SPACING = 0.80, R = 4.6;
|
| 518 |
+
const totalH = (L - 1) * SPACING;
|
| 519 |
+
const featureMeta = [];
|
| 520 |
+
const FLAT_W = R * 2.4; // horizontal span for features in flat mode
|
| 521 |
+
|
| 522 |
+
// ── Stage bands ─────────────────────────────────────────────
|
| 523 |
+
if (!d.dissolved) {
|
| 524 |
+
d.stageBands.forEach(([d0, d1, si]) => {
|
| 525 |
+
const y0 = d0 * totalH - totalH / 2;
|
| 526 |
+
const y1 = d1 * totalH - totalH / 2;
|
| 527 |
+
const h = Math.max(y1 - y0, 0.08);
|
| 528 |
+
if (viewMode === 'flat') {
|
| 529 |
+
// Horizontal stripe across the circuit
|
| 530 |
+
const geo = new THREE.PlaneGeometry(FLAT_W * 1.15, h);
|
| 531 |
+
const mat = new THREE.MeshBasicMaterial({ color: STAGE_COLORS[si], transparent: true, opacity: 0.085, depthWrite: false, side: THREE.DoubleSide });
|
| 532 |
+
const mesh = new THREE.Mesh(geo, mat);
|
| 533 |
+
mesh.position.set(0, (y0 + y1) / 2, -0.02);
|
| 534 |
+
group.add(mesh);
|
| 535 |
+
} else {
|
| 536 |
+
// 3D box slab + capping rings
|
| 537 |
+
const geo = new THREE.BoxGeometry(R * 2.5, h, R * 2.5);
|
| 538 |
+
const mat = new THREE.MeshBasicMaterial({ color: STAGE_COLORS[si], transparent: true, opacity: 0.032, depthWrite: false, side: THREE.DoubleSide });
|
| 539 |
+
const mesh = new THREE.Mesh(geo, mat);
|
| 540 |
+
mesh.position.y = (y0 + y1) / 2;
|
| 541 |
+
group.add(mesh);
|
| 542 |
+
[y0, y1].forEach(y => {
|
| 543 |
+
const rg = new THREE.RingGeometry(R * 1.22, R * 1.24, 64);
|
| 544 |
+
const rm = new THREE.MeshBasicMaterial({ color: STAGE_COLORS[si], transparent: true, opacity: 0.20, side: THREE.DoubleSide, depthWrite: false });
|
| 545 |
+
const ring = new THREE.Mesh(rg, rm);
|
| 546 |
+
ring.position.y = y; ring.rotation.x = -Math.PI / 2;
|
| 547 |
+
group.add(ring);
|
| 548 |
+
});
|
| 549 |
+
}
|
| 550 |
+
});
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
// ── Layer guides (rings in 3D, lines in flat) ───────────────
|
| 554 |
+
for (let li = 0; li < L; li++) {
|
| 555 |
+
const y = (li / (L - 1)) * totalH - totalH / 2;
|
| 556 |
+
let lineColor, lineOpacity;
|
| 557 |
+
if (d.dissolved) { lineColor = new THREE.Color(0x330a0a); lineOpacity = 0.22; }
|
| 558 |
+
else if (d.c4 !== null && d.c4 > 1.0) { lineColor = new THREE.Color(0xff4400); lineOpacity = 0.24; }
|
| 559 |
+
else if (d.c4 !== null) {
|
| 560 |
+
const t = Math.min(d.c4 / 0.55, 1.0);
|
| 561 |
+
lineColor = new THREE.Color().lerpColors(new THREE.Color(0x1a2d6e), new THREE.Color(0xff8822), t);
|
| 562 |
+
lineOpacity = 0.14;
|
| 563 |
+
} else { lineColor = new THREE.Color(0x1e2a50); lineOpacity = 0.10; }
|
| 564 |
+
|
| 565 |
+
if (viewMode === 'flat') {
|
| 566 |
+
const lineGeo = new THREE.BufferGeometry().setFromPoints([
|
| 567 |
+
new THREE.Vector3(-FLAT_W / 2, y, -0.05),
|
| 568 |
+
new THREE.Vector3( FLAT_W / 2, y, -0.05),
|
| 569 |
+
]);
|
| 570 |
+
const lineMat = new THREE.LineBasicMaterial({ color: lineColor, transparent: true, opacity: lineOpacity });
|
| 571 |
+
group.add(new THREE.Line(lineGeo, lineMat));
|
| 572 |
+
} else {
|
| 573 |
+
const rg = new THREE.RingGeometry(R * 0.88, R, 64);
|
| 574 |
+
const rm = new THREE.MeshBasicMaterial({ color: lineColor, transparent: true, opacity: lineOpacity, side: THREE.DoubleSide, depthWrite: false });
|
| 575 |
+
const ring = new THREE.Mesh(rg, rm); ring.position.y = y; ring.rotation.x = -Math.PI / 2;
|
| 576 |
+
group.add(ring);
|
| 577 |
+
}
|
| 578 |
+
}
|
| 579 |
+
|
| 580 |
+
// ── Feature points (InstancedMesh, layout depends on viewMode) ──
|
| 581 |
+
const totalF = L * F;
|
| 582 |
+
const geo = new THREE.SphereGeometry(0.08, 7, 7);
|
| 583 |
+
const mat = new THREE.MeshStandardMaterial({ roughness: 0.3, metalness: 0.05 });
|
| 584 |
+
const instanceMesh = new THREE.InstancedMesh(geo, mat, totalF);
|
| 585 |
+
instanceMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
|
| 586 |
+
|
| 587 |
+
const dummy = new THREE.Object3D();
|
| 588 |
+
const color = new THREE.Color();
|
| 589 |
+
const rng = mulberry32(42 + key.charCodeAt(0) * 7);
|
| 590 |
+
|
| 591 |
+
let idx = 0;
|
| 592 |
+
for (let li = 0; li < L; li++) {
|
| 593 |
+
const y = (li / (L - 1)) * totalH - totalH / 2;
|
| 594 |
+
const depth = li / (L - 1);
|
| 595 |
+
const stage = d.dissolved ? 5 : getStageForDepth(d.stageBands, depth);
|
| 596 |
+
|
| 597 |
+
for (let fi = 0; fi < F; fi++) {
|
| 598 |
+
let x, z;
|
| 599 |
+
if (viewMode === 'flat') {
|
| 600 |
+
// 2D circuit: horizontal line per layer; features spread left-to-right.
|
| 601 |
+
// Dissolved: jitter the ordering to show lack of structure.
|
| 602 |
+
if (d.dissolved) {
|
| 603 |
+
x = (rng() - 0.5) * FLAT_W;
|
| 604 |
+
} else {
|
| 605 |
+
x = ((fi + 0.5) / F - 0.5) * FLAT_W;
|
| 606 |
+
}
|
| 607 |
+
z = 0;
|
| 608 |
+
} else if (d.dissolved) {
|
| 609 |
+
const angle = rng() * Math.PI * 2;
|
| 610 |
+
const r = R * 0.82 * Math.sqrt(rng());
|
| 611 |
+
x = r * Math.cos(angle); z = r * Math.sin(angle);
|
| 612 |
+
} else {
|
| 613 |
+
const theta = fi * 2.399963229;
|
| 614 |
+
const r = R * 0.84 * Math.sqrt((fi + 0.5) / F);
|
| 615 |
+
x = r * Math.cos(theta); z = r * Math.sin(theta);
|
| 616 |
+
}
|
| 617 |
+
const jitter = viewMode === 'flat'
|
| 618 |
+
? (rng() - 0.5) * (d.dissolved ? 0.10 : 0.04)
|
| 619 |
+
: (rng() - 0.5) * (d.dissolved ? 0.18 : 0.06);
|
| 620 |
+
dummy.position.set(x, y + jitter, z);
|
| 621 |
+
const baseScale = viewMode === 'flat' ? 0.85 : 1.0;
|
| 622 |
+
dummy.scale.setScalar(baseScale * (d.dissolved ? (0.6 + rng() * 0.9) : (0.8 + rng() * 0.65)));
|
| 623 |
+
dummy.updateMatrix();
|
| 624 |
+
instanceMesh.setMatrixAt(idx, dummy.matrix);
|
| 625 |
+
color.copy(STAGE_COLORS[stage]);
|
| 626 |
+
color.offsetHSL(0, (rng() - 0.5) * 0.12, (rng() - 0.5) * 0.14);
|
| 627 |
+
instanceMesh.setColorAt(idx, color);
|
| 628 |
+
featureMeta[idx] = { li, fi, stage, y, x, z, key };
|
| 629 |
+
idx++;
|
| 630 |
+
}
|
| 631 |
+
}
|
| 632 |
+
instanceMesh.instanceMatrix.needsUpdate = true;
|
| 633 |
+
if (instanceMesh.instanceColor) instanceMesh.instanceColor.needsUpdate = true;
|
| 634 |
+
group.add(instanceMesh);
|
| 635 |
+
|
| 636 |
+
// ── Inter-layer edges (flat mode only) — show top features connecting consecutive layers ──
|
| 637 |
+
if (viewMode === 'flat' && !d.dissolved) {
|
| 638 |
+
const edgePts = [];
|
| 639 |
+
const edgeColors = [];
|
| 640 |
+
const TOP_K = Math.min(8, F); // strongest few features per layer
|
| 641 |
+
for (let li = 0; li < L - 1; li++) {
|
| 642 |
+
const y0 = (li / (L - 1)) * totalH - totalH / 2;
|
| 643 |
+
const y1 = ((li+1) / (L - 1)) * totalH - totalH / 2;
|
| 644 |
+
const stage0 = getStageForDepth(d.stageBands, li / (L - 1));
|
| 645 |
+
const c = STAGE_COLORS[stage0];
|
| 646 |
+
for (let k = 0; k < TOP_K; k++) {
|
| 647 |
+
const x0 = ((k + 0.5) / F - 0.5) * FLAT_W;
|
| 648 |
+
const x1 = ((k + 0.5) / F - 0.5) * FLAT_W;
|
| 649 |
+
edgePts.push(x0, y0, 0, x1, y1, 0);
|
| 650 |
+
edgeColors.push(c.r, c.g, c.b, c.r, c.g, c.b);
|
| 651 |
+
}
|
| 652 |
+
}
|
| 653 |
+
const eg = new THREE.BufferGeometry();
|
| 654 |
+
eg.setAttribute('position', new THREE.Float32BufferAttribute(edgePts, 3));
|
| 655 |
+
eg.setAttribute('color', new THREE.Float32BufferAttribute(edgeColors, 3));
|
| 656 |
+
const em = new THREE.LineBasicMaterial({ vertexColors: true, transparent: true, opacity: 0.18 });
|
| 657 |
+
group.add(new THREE.LineSegments(eg, em));
|
| 658 |
+
}
|
| 659 |
+
|
| 660 |
+
scene.add(group);
|
| 661 |
+
groups[key] = { group, instanceMesh, featureMeta, totalH };
|
| 662 |
+
return groups[key];
|
| 663 |
+
}
|
| 664 |
+
|
| 665 |
+
// ── Info panel update ─────────────────────────────────────────
|
| 666 |
+
function c4Class(v) {
|
| 667 |
+
if (v === null) return 'dim';
|
| 668 |
+
if (v < 0.06) return 'good';
|
| 669 |
+
if (v < 0.5) return 'warn';
|
| 670 |
+
return 'alert';
|
| 671 |
+
}
|
| 672 |
+
|
| 673 |
+
function barWidth(v, max) {
|
| 674 |
+
if (v === null) return 0;
|
| 675 |
+
return Math.min((v / max) * 100, 100);
|
| 676 |
+
}
|
| 677 |
+
|
| 678 |
+
function updateInfoPanel(key) {
|
| 679 |
+
const d = VINDEX_DATA[key];
|
| 680 |
+
|
| 681 |
+
document.getElementById('m-name').textContent = d.name;
|
| 682 |
+
|
| 683 |
+
const hfEl = document.getElementById('m-hf-link');
|
| 684 |
+
if (d.hfRepo) {
|
| 685 |
+
hfEl.href = `https://huggingface.co/${d.hfRepo}`;
|
| 686 |
+
hfEl.style.display = '';
|
| 687 |
+
} else {
|
| 688 |
+
hfEl.style.display = 'none';
|
| 689 |
+
}
|
| 690 |
+
|
| 691 |
+
document.getElementById('m-meta').textContent = `${d.org} · ${d.params} · ${d.precision} · ${d.layers}L`;
|
| 692 |
+
|
| 693 |
+
// C4
|
| 694 |
+
const c4El = document.getElementById('m-c4-val');
|
| 695 |
+
const c4Chip = document.getElementById('m-c4-chip');
|
| 696 |
+
if (d.c4 !== null) {
|
| 697 |
+
c4El.textContent = d.c4.toFixed(3);
|
| 698 |
+
c4El.className = 'c4-val ' + c4Class(d.c4);
|
| 699 |
+
if (d.c4 < 0.06) {
|
| 700 |
+
c4Chip.textContent = '✓ universal constant';
|
| 701 |
+
c4Chip.className = 'c4-chip chip-good';
|
| 702 |
+
} else if (d.c4 > 1.0) {
|
| 703 |
+
c4Chip.textContent = `⚠️ ${(d.c4/0.042).toFixed(0)}× anomaly`;
|
| 704 |
+
c4Chip.className = 'c4-chip chip-alert';
|
| 705 |
+
} else {
|
| 706 |
+
const mult = (d.c4 / 0.042).toFixed(0);
|
| 707 |
+
c4Chip.textContent = `↑ ${mult}× constant`;
|
| 708 |
+
c4Chip.className = 'c4-chip chip-warn';
|
| 709 |
+
}
|
| 710 |
+
} else {
|
| 711 |
+
c4El.textContent = 'N/A';
|
| 712 |
+
c4El.className = 'c4-val';
|
| 713 |
+
c4Chip.textContent = 'Phase 2 pending';
|
| 714 |
+
c4Chip.className = 'c4-chip chip-warn';
|
| 715 |
+
}
|
| 716 |
+
|
| 717 |
+
// C5 stage bar
|
| 718 |
+
const stageFill = document.getElementById('m-stage-fill');
|
| 719 |
+
const c5Val = document.getElementById('m-c5-val');
|
| 720 |
+
if (d.c5 !== null) {
|
| 721 |
+
const pct = (d.c5 / d.c5max) * 100;
|
| 722 |
+
stageFill.style.width = pct + '%';
|
| 723 |
+
if (d.c5 === 1) {
|
| 724 |
+
stageFill.style.background = '#ff4444';
|
| 725 |
+
c5Val.textContent = '💀 C5=1';
|
| 726 |
+
c5Val.className = 'stage-bar-val';
|
| 727 |
+
c5Val.style.color = '#ff4444';
|
| 728 |
+
} else {
|
| 729 |
+
stageFill.style.background = '#44ffaa';
|
| 730 |
+
c5Val.textContent = `${d.c5} stages`;
|
| 731 |
+
c5Val.className = 'stage-bar-val';
|
| 732 |
+
c5Val.style.color = '#44ffaa';
|
| 733 |
+
}
|
| 734 |
+
} else {
|
| 735 |
+
stageFill.style.width = '0%';
|
| 736 |
+
c5Val.textContent = 'N/A';
|
| 737 |
+
c5Val.style.color = '';
|
| 738 |
+
}
|
| 739 |
+
|
| 740 |
+
// Gate 3
|
| 741 |
+
const gate3Icon = document.getElementById('m-gate3-icon');
|
| 742 |
+
const gate3Label = document.getElementById('m-gate3-label');
|
| 743 |
+
const g3 = d.gate3;
|
| 744 |
+
if (g3 === 'PASS') {
|
| 745 |
+
gate3Icon.textContent = '✅';
|
| 746 |
+
gate3Label.textContent = `Gate-3: ${d.gate3note}`;
|
| 747 |
+
gate3Label.style.color = '#44ffaa'; gate3Label.style.opacity = '1';
|
| 748 |
+
} else if (g3 === 'PENDING') {
|
| 749 |
+
gate3Icon.textContent = '⏳';
|
| 750 |
+
gate3Label.textContent = `Gate-3: ${d.gate3note}`;
|
| 751 |
+
gate3Label.style.color = ''; gate3Label.style.opacity = '0.5';
|
| 752 |
+
} else {
|
| 753 |
+
gate3Icon.textContent = '○';
|
| 754 |
+
gate3Label.textContent = `Gate-3: ${d.gate3note}`;
|
| 755 |
+
gate3Label.style.color = ''; gate3Label.style.opacity = '0.35';
|
| 756 |
+
}
|
| 757 |
+
|
| 758 |
+
// Anomaly banner
|
| 759 |
+
const anomalyEl = document.getElementById('m-anomaly');
|
| 760 |
+
if (d.anomaly) {
|
| 761 |
+
anomalyEl.textContent = d.anomaly;
|
| 762 |
+
anomalyEl.style.display = 'block';
|
| 763 |
+
anomalyEl.style.background = d.anomalyType === 'dead'
|
| 764 |
+
? 'rgba(255,40,40,0.1)' : 'rgba(255,120,50,0.1)';
|
| 765 |
+
anomalyEl.style.borderColor = d.anomalyType === 'dead'
|
| 766 |
+
? 'rgba(255,40,40,0.3)' : 'rgba(255,120,50,0.3)';
|
| 767 |
+
anomalyEl.style.color = d.anomalyType === 'dead' ? '#ff7777' : '#ffaa66';
|
| 768 |
+
} else {
|
| 769 |
+
anomalyEl.style.display = 'none';
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
// Tier 2 metrics
|
| 773 |
+
function setBar(barId, valId, v, max, cls) {
|
| 774 |
+
document.getElementById(barId).style.width = barWidth(v, max) + '%';
|
| 775 |
+
const el = document.getElementById(valId);
|
| 776 |
+
el.textContent = v !== null ? v.toFixed(3) : 'N/A';
|
| 777 |
+
el.className = 'm-val ' + (v !== null ? (cls || '') : 'dim');
|
| 778 |
+
}
|
| 779 |
+
setBar('b-c1','v-c1', d.c1, 0.5, d.c1 > 0.3 ? 'warn' : 'good');
|
| 780 |
+
setBar('b-c2','v-c2', d.c2, 1.0, d.c2 !== null && d.c2 > 0.85 ? 'good' : '');
|
| 781 |
+
setBar('b-c3','v-c3', d.c3, 1.0, d.c3 !== null && d.c3 > 0.75 ? 'good' : 'warn');
|
| 782 |
+
setBar('b-c4','v-c4', d.c4 !== null ? Math.min(d.c4, 2.0) : null, 2.0, c4Class(d.c4));
|
| 783 |
+
document.getElementById('b-spec').style.width = (d.spectrumScore * 100) + '%';
|
| 784 |
+
const specEl = document.getElementById('v-spec');
|
| 785 |
+
specEl.textContent = d.spectrumScore > 0.7 ? 'power-law' : (d.spectrumScore > 0.2 ? 'partial' : 'flat');
|
| 786 |
+
specEl.className = 'm-val ' + (d.spectrumScore > 0.7 ? 'good' : d.spectrumScore > 0.2 ? 'warn' : 'alert');
|
| 787 |
+
|
| 788 |
+
const shortidEl = document.getElementById('v-shortid');
|
| 789 |
+
shortidEl.textContent = d.shortId || '—';
|
| 790 |
+
|
| 791 |
+
document.getElementById('copy-shortid').onclick = () => {
|
| 792 |
+
const txt = d.hfRepo ? `https://huggingface.co/${d.hfRepo}` : (d.shortId || '');
|
| 793 |
+
navigator.clipboard.writeText(txt).catch(() => {});
|
| 794 |
+
};
|
| 795 |
+
}
|
| 796 |
+
|
| 797 |
+
// ── Compare panel ─────────────────────────────────────────────
|
| 798 |
+
function makeCmpCol(colId, key) {
|
| 799 |
+
const d = VINDEX_DATA[key];
|
| 800 |
+
const col = document.getElementById(colId);
|
| 801 |
+
col.innerHTML = '';
|
| 802 |
+
|
| 803 |
+
const title = document.createElement('div');
|
| 804 |
+
title.className = 'cmp-title';
|
| 805 |
+
title.textContent = d.name;
|
| 806 |
+
col.appendChild(title);
|
| 807 |
+
|
| 808 |
+
const meta = document.createElement('div');
|
| 809 |
+
meta.className = 'cmp-meta';
|
| 810 |
+
meta.textContent = `${d.org} · ${d.params} · ${d.precision}`;
|
| 811 |
+
col.appendChild(meta);
|
| 812 |
+
|
| 813 |
+
const rows = [
|
| 814 |
+
['C4 Temp', d.c4 !== null ? d.c4.toFixed(3) : 'N/A', c4Class(d.c4)],
|
| 815 |
+
['C5 Stages', d.c5 !== null ? (d.c5 === 1 ? '💀 1' : String(d.c5)) : 'N/A', d.c5 === 1 ? 'alert' : 'good'],
|
| 816 |
+
['C3 Coherence', d.c3 !== null ? d.c3.toFixed(3) : 'N/A', d.c3 !== null && d.c3 > 0.75 ? 'good' : 'warn'],
|
| 817 |
+
['C1 Sparsity', d.c1 !== null ? d.c1.toFixed(3) : 'N/A', ''],
|
| 818 |
+
['Spectrum', d.spectrumScore > 0.7 ? 'power-law ✓' : (d.spectrumScore > 0.2 ? 'partial' : 'flat ⚠️'), d.spectrumScore > 0.7 ? 'good' : 'alert'],
|
| 819 |
+
];
|
| 820 |
+
rows.forEach(([label, val, cls]) => {
|
| 821 |
+
const row = document.createElement('div');
|
| 822 |
+
row.className = 'cmp-metric';
|
| 823 |
+
const lEl = document.createElement('span');
|
| 824 |
+
lEl.style.opacity = '0.45'; lEl.textContent = label;
|
| 825 |
+
const vEl = document.createElement('span');
|
| 826 |
+
vEl.className = 'cmp-val ' + cls;
|
| 827 |
+
vEl.textContent = val;
|
| 828 |
+
row.appendChild(lEl); row.appendChild(vEl);
|
| 829 |
+
col.appendChild(row);
|
| 830 |
+
});
|
| 831 |
+
}
|
| 832 |
+
|
| 833 |
+
// ── Show single model ─────────────────────────────────────────
|
| 834 |
+
function showSingle(key) {
|
| 835 |
+
compareMode = false;
|
| 836 |
+
document.getElementById('compare-btn').classList.remove('active');
|
| 837 |
+
document.getElementById('compare-panel').style.display = 'none';
|
| 838 |
+
document.getElementById('info-panel').style.display = '';
|
| 839 |
+
document.getElementById('legend').style.display = '';
|
| 840 |
+
|
| 841 |
+
Object.keys(groups).filter(k => k !== key).forEach(k => {
|
| 842 |
+
scene.remove(groups[k].group); delete groups[k];
|
| 843 |
+
});
|
| 844 |
+
clearWalk();
|
| 845 |
+
activeKeys = [key];
|
| 846 |
+
buildGroup(key, 0);
|
| 847 |
+
updateInfoPanel(key);
|
| 848 |
+
|
| 849 |
+
const L = VINDEX_DATA[key].layers;
|
| 850 |
+
if (viewMode === 'flat') {
|
| 851 |
+
// Need to fit (L-1) * 0.80 vertical units; FOV 52 -> visible_h ~ 0.976 * z
|
| 852 |
+
const totalH = (L - 1) * 0.80;
|
| 853 |
+
camera.position.set(0, 0, Math.max(20, totalH * 1.25 + 6));
|
| 854 |
+
} else {
|
| 855 |
+
camera.position.set(0, 2, 20 + L * 0.22);
|
| 856 |
+
}
|
| 857 |
+
controls.target.set(0, 0, 0);
|
| 858 |
+
controls.update();
|
| 859 |
+
}
|
| 860 |
+
|
| 861 |
+
// ── Compare mode ──────────────────────────────────────────────
|
| 862 |
+
function showCompare(keyA, keyB) {
|
| 863 |
+
compareMode = true;
|
| 864 |
+
document.getElementById('compare-btn').classList.add('active');
|
| 865 |
+
document.getElementById('compare-panel').style.display = '';
|
| 866 |
+
document.getElementById('info-panel').style.display = 'none';
|
| 867 |
+
document.getElementById('legend').style.display = 'none';
|
| 868 |
+
|
| 869 |
+
Object.keys(groups).filter(k => k !== keyA && k !== keyB).forEach(k => {
|
| 870 |
+
scene.remove(groups[k].group); delete groups[k];
|
| 871 |
+
});
|
| 872 |
+
clearWalk();
|
| 873 |
+
activeKeys = [keyA, keyB];
|
| 874 |
+
buildGroup(keyA, -13);
|
| 875 |
+
buildGroup(keyB, 13);
|
| 876 |
+
makeCmpCol('cmp-a', keyA);
|
| 877 |
+
makeCmpCol('cmp-b', keyB);
|
| 878 |
+
|
| 879 |
+
const maxL = Math.max(VINDEX_DATA[keyA].layers, VINDEX_DATA[keyB].layers);
|
| 880 |
+
if (viewMode === 'flat') {
|
| 881 |
+
const totalH = (maxL - 1) * 0.80;
|
| 882 |
+
camera.position.set(0, 0, Math.max(28, totalH * 1.4 + 8));
|
| 883 |
+
} else {
|
| 884 |
+
camera.position.set(0, 2, 30 + maxL * 0.18);
|
| 885 |
+
}
|
| 886 |
+
controls.target.set(0, 0, 0);
|
| 887 |
+
controls.update();
|
| 888 |
+
}
|
| 889 |
+
|
| 890 |
+
// ── Walk traces ───────────────────────────────────────────────
|
| 891 |
+
function clearWalk() {
|
| 892 |
+
walkLines.forEach(l => { scene.remove(l); l.geometry.dispose(); l.material.dispose(); });
|
| 893 |
+
walkLines = [];
|
| 894 |
+
selectedIdx = -1;
|
| 895 |
+
}
|
| 896 |
+
|
| 897 |
+
function traceWalk(globalIdx, instanceMesh, featureMeta, groupObj) {
|
| 898 |
+
clearWalk();
|
| 899 |
+
const meta = featureMeta[globalIdx];
|
| 900 |
+
if (!meta) return;
|
| 901 |
+
const d = VINDEX_DATA[meta.key];
|
| 902 |
+
const totalH = (d.layers - 1) * 0.80;
|
| 903 |
+
const rng = mulberry32(globalIdx * 31337);
|
| 904 |
+
const offsetX = groupObj.position.x;
|
| 905 |
+
let px = meta.x + offsetX, py = meta.y, pz = meta.z;
|
| 906 |
+
const stageColor = STAGE_COLORS[meta.stage];
|
| 907 |
+
const steps = d.dissolved ? 6 : 10;
|
| 908 |
+
for (let li = meta.li + 1; li < Math.min(meta.li + steps, d.layers); li++) {
|
| 909 |
+
const y = (li / (d.layers - 1)) * totalH - totalH / 2;
|
| 910 |
+
let x, z;
|
| 911 |
+
if (d.dissolved) {
|
| 912 |
+
x = (rng() - 0.5) * 8 + offsetX; z = (rng() - 0.5) * 8;
|
| 913 |
+
} else {
|
| 914 |
+
const angle = Math.atan2(pz, px - offsetX) + (rng() - 0.5) * 0.3;
|
| 915 |
+
const r = Math.sqrt((px-offsetX)**2 + pz**2);
|
| 916 |
+
x = r * Math.cos(angle) + (rng()-0.5)*0.25 + offsetX;
|
| 917 |
+
z = r * Math.sin(angle) + (rng()-0.5)*0.25;
|
| 918 |
+
}
|
| 919 |
+
const pts = [new THREE.Vector3(px,py,pz), new THREE.Vector3(x,y,z)];
|
| 920 |
+
const line = new THREE.Line(
|
| 921 |
+
new THREE.BufferGeometry().setFromPoints(pts),
|
| 922 |
+
new THREE.LineBasicMaterial({ color: stageColor, transparent: true, opacity: d.dissolved ? 0.22 : (0.55 - (li-meta.li)*0.04) })
|
| 923 |
+
);
|
| 924 |
+
scene.add(line); walkLines.push(line);
|
| 925 |
+
px = x; py = y; pz = z;
|
| 926 |
+
}
|
| 927 |
+
instanceMesh.setColorAt(globalIdx, new THREE.Color(0xffffff));
|
| 928 |
+
instanceMesh.instanceColor.needsUpdate = true;
|
| 929 |
+
selectedIdx = globalIdx;
|
| 930 |
+
}
|
| 931 |
+
|
| 932 |
+
// ── Tooltip ───────────────────────────────────────────────────
|
| 933 |
+
const tooltip = document.getElementById('tooltip');
|
| 934 |
+
function showTooltip(ex, ey, meta) {
|
| 935 |
+
const d = VINDEX_DATA[meta.key];
|
| 936 |
+
const stage = STAGE_NAMES[meta.stage];
|
| 937 |
+
const depth = ((meta.li / (d.layers - 1)) * 100).toFixed(0);
|
| 938 |
+
tooltip.innerHTML = '';
|
| 939 |
+
[[`${d.name}`, 'bold'], [`Layer ${meta.li} (${depth}% depth) · Feature ${meta.fi}`, ''], [`Stage: ${stage}`, meta.stage === 5 ? 'color:#ff4444' : 'color:#44ffaa']].forEach(([text, style]) => {
|
| 940 |
+
const el = document.createElement('div');
|
| 941 |
+
el.textContent = text;
|
| 942 |
+
if (style.includes(':')) el.setAttribute('style', style);
|
| 943 |
+
else if (style === 'bold') el.style.fontWeight = 'bold';
|
| 944 |
+
tooltip.appendChild(el);
|
| 945 |
+
});
|
| 946 |
+
tooltip.style.left = (ex + 16) + 'px';
|
| 947 |
+
tooltip.style.top = (ey - 52) + 'px';
|
| 948 |
+
tooltip.style.display = 'block';
|
| 949 |
+
}
|
| 950 |
+
|
| 951 |
+
// ── Demo sequence ─────────────────────────────────────────────
|
| 952 |
+
const demoOverlay = document.getElementById('demo-overlay');
|
| 953 |
+
function setDemoText(text) {
|
| 954 |
+
demoOverlay.textContent = text;
|
| 955 |
+
demoOverlay.style.display = text ? 'block' : 'none';
|
| 956 |
+
}
|
| 957 |
+
|
| 958 |
+
async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
| 959 |
+
|
| 960 |
+
async function runDemo() {
|
| 961 |
+
if (demoRunning) return;
|
| 962 |
+
demoRunning = true;
|
| 963 |
+
document.getElementById('demo-btn').textContent = '■ Stop';
|
| 964 |
+
controls.enabled = false;
|
| 965 |
+
|
| 966 |
+
// 0-2s: materialize Qwen3-8B
|
| 967 |
+
showSingle('qwen3-8b');
|
| 968 |
+
await sleep(100);
|
| 969 |
+
setDemoText('Each column: a transformer model\'s learned features, organized by layer');
|
| 970 |
+
camera.position.set(0, -6, 28);
|
| 971 |
+
await sleep(2200);
|
| 972 |
+
|
| 973 |
+
// 2-4s: orbit
|
| 974 |
+
setDemoText('128 features per layer, arranged by singular value direction');
|
| 975 |
+
camera.position.set(12, 3, 22);
|
| 976 |
+
await sleep(2000);
|
| 977 |
+
|
| 978 |
+
// 4-6s: show stage bands label
|
| 979 |
+
setDemoText('Four circuit stages emerge at consistent relative depths — across every architecture');
|
| 980 |
+
camera.position.set(0, 4, 22);
|
| 981 |
+
await sleep(2400);
|
| 982 |
+
|
| 983 |
+
// 6-8s: zoom entity zone
|
| 984 |
+
setDemoText('At ~50% depth: entity commitment. The model decides what it\'s talking about.');
|
| 985 |
+
camera.position.set(3, 0, 14);
|
| 986 |
+
controls.target.set(0, 0, 0);
|
| 987 |
+
await sleep(2400);
|
| 988 |
+
|
| 989 |
+
// 8-10s: compare mode reveal
|
| 990 |
+
setDemoText('');
|
| 991 |
+
await sleep(300);
|
| 992 |
+
showCompare('qwen3-8b', 'bonsai-1bit');
|
| 993 |
+
camera.position.set(0, 2, 36);
|
| 994 |
+
await sleep(400);
|
| 995 |
+
setDemoText('Until the model is 1-bit. The circuit dissolves entirely.');
|
| 996 |
+
await sleep(2200);
|
| 997 |
+
|
| 998 |
+
// 10-12s: hold + tagline
|
| 999 |
+
setDemoText('Universal structure. Measurable. Editable.');
|
| 1000 |
+
await sleep(2500);
|
| 1001 |
+
|
| 1002 |
+
// done
|
| 1003 |
+
setDemoText('');
|
| 1004 |
+
controls.enabled = true;
|
| 1005 |
+
demoRunning = false;
|
| 1006 |
+
document.getElementById('demo-btn').textContent = '▶ Demo';
|
| 1007 |
+
}
|
| 1008 |
+
|
| 1009 |
+
// ── Raycasting ────────────────────────────────────────────────
|
| 1010 |
+
const raycaster = new THREE.Raycaster();
|
| 1011 |
+
const mouse = new THREE.Vector2();
|
| 1012 |
+
|
| 1013 |
+
renderer.domElement.addEventListener('click', e => {
|
| 1014 |
+
if (demoRunning) return;
|
| 1015 |
+
mouse.x = (e.clientX / innerWidth) * 2 - 1;
|
| 1016 |
+
mouse.y = -(e.clientY / innerHeight) * 2 + 1;
|
| 1017 |
+
raycaster.setFromCamera(mouse, camera);
|
| 1018 |
+
let bestHit = null, bestMesh = null, bestMeta = null, bestGroup = null;
|
| 1019 |
+
for (const key of activeKeys) {
|
| 1020 |
+
const g = groups[key];
|
| 1021 |
+
if (!g) continue;
|
| 1022 |
+
const hits = raycaster.intersectObject(g.instanceMesh);
|
| 1023 |
+
if (hits.length > 0 && (!bestHit || hits[0].distance < bestHit.distance)) {
|
| 1024 |
+
bestHit = hits[0]; bestMesh = g.instanceMesh; bestMeta = g.featureMeta; bestGroup = g.group;
|
| 1025 |
+
}
|
| 1026 |
+
}
|
| 1027 |
+
if (bestHit) {
|
| 1028 |
+
traceWalk(bestHit.instanceId, bestMesh, bestMeta, bestGroup);
|
| 1029 |
+
showTooltip(e.clientX, e.clientY, bestMeta[bestHit.instanceId]);
|
| 1030 |
+
} else {
|
| 1031 |
+
clearWalk(); tooltip.style.display = 'none';
|
| 1032 |
+
}
|
| 1033 |
+
});
|
| 1034 |
+
renderer.domElement.addEventListener('mousemove', () => { tooltip.style.display = 'none'; });
|
| 1035 |
+
|
| 1036 |
+
// ── Entity search ──────────────────────────────────────────────
|
| 1037 |
+
// Two-tier search backend:
|
| 1038 |
+
// 1. REAL: `search_index.json` per model from `notebooks/build_search_index.py` Modal pipeline —
|
| 1039 |
+
// ~5000 whole-word tokens, each mapped to top-8 (layer, feature, score) tuples from probing
|
| 1040 |
+
// the actual transformer's gate*up activations projected onto the vindex SVD basis.
|
| 1041 |
+
// Loaded lazily per model; cached in `realIndices`. Files served from /search-indexes/{slug}.json.
|
| 1042 |
+
// 2. DEMO fallback (below): hand-curated handful of tokens (Paris→capital from the Gate-3 result,
|
| 1043 |
+
// plus illustrative matches) — used when the real index hasn't been built+deployed for a model.
|
| 1044 |
+
|
| 1045 |
+
// Map viewer model keys to slugs that match build_search_index.py's slug() output.
|
| 1046 |
+
const MODEL_KEY_TO_SLUG = {
|
| 1047 |
+
'gemma-4-e2b': 'google-gemma-4-e2b-it',
|
| 1048 |
+
'qwen3-0.6b': 'qwen-qwen3-0-6b',
|
| 1049 |
+
'qwen3-8b': 'qwen-qwen3-8b',
|
| 1050 |
+
'qwen3.6-35b': 'qwen-qwen3-6-35b-a3b',
|
| 1051 |
+
'llama-3.1-8b': 'meta-llama-llama-3-1-8b-instruct',
|
| 1052 |
+
'gpt-oss-120b': 'openai-gpt-oss-120b',
|
| 1053 |
+
'ministral-3b': 'mistralai-ministral-3-3b-instruct-2512',
|
| 1054 |
+
'medgemma-4b': 'google-medgemma-1-5-4b-it',
|
| 1055 |
+
'bonsai-1bit': 'prism-ml-bonsai-8b-unpacked',
|
| 1056 |
+
};
|
| 1057 |
+
const realIndices = new Map(); // slug → { tokens, matches } loaded JSON
|
| 1058 |
+
const realIndexAttempts = new Set(); // slugs already tried (don't refetch on failure)
|
| 1059 |
+
|
| 1060 |
+
async function loadRealSearchIndex(modelKey) {
|
| 1061 |
+
const slug = MODEL_KEY_TO_SLUG[modelKey];
|
| 1062 |
+
if (!slug || realIndices.has(slug) || realIndexAttempts.has(slug)) return;
|
| 1063 |
+
realIndexAttempts.add(slug);
|
| 1064 |
+
try {
|
| 1065 |
+
const url = `/search-indexes/${slug}.json`;
|
| 1066 |
+
const res = await fetch(url);
|
| 1067 |
+
if (!res.ok) {
|
| 1068 |
+
console.info(`[search] no real index for ${modelKey} — using demo dataset`);
|
| 1069 |
+
return;
|
| 1070 |
+
}
|
| 1071 |
+
const idx = await res.json();
|
| 1072 |
+
realIndices.set(slug, idx);
|
| 1073 |
+
console.info(`[search] loaded real index: ${idx.n_tokens} tokens for ${modelKey}`);
|
| 1074 |
+
// Re-run current query against the new index
|
| 1075 |
+
const q = document.getElementById('search-input').value.trim();
|
| 1076 |
+
if (q) highlightSearch(q);
|
| 1077 |
+
} catch (err) {
|
| 1078 |
+
console.warn(`[search] fetch failed for ${modelKey}:`, err);
|
| 1079 |
+
}
|
| 1080 |
+
}
|
| 1081 |
+
|
| 1082 |
+
// Look up matches for a query in the real index. Strategy:
|
| 1083 |
+
// 1. exact token match → return its top-K (layer, feature, score)
|
| 1084 |
+
// 2. else: substring match across all tokens, collect from up to 5 closest
|
| 1085 |
+
// 3. returns array of { layerNorm, fi, label } compatible with demo format
|
| 1086 |
+
function lookupReal(modelKey, query) {
|
| 1087 |
+
const slug = MODEL_KEY_TO_SLUG[modelKey];
|
| 1088 |
+
const idx = realIndices.get(slug);
|
| 1089 |
+
if (!idx) return null;
|
| 1090 |
+
const q = query.toLowerCase().trim();
|
| 1091 |
+
const hits = [];
|
| 1092 |
+
// Exact match first
|
| 1093 |
+
if (idx.matches[q]) {
|
| 1094 |
+
for (const m of idx.matches[q]) {
|
| 1095 |
+
hits.push({
|
| 1096 |
+
layerNorm: m.layer / (idx.num_layers - 1),
|
| 1097 |
+
fi: m.feature,
|
| 1098 |
+
label: `score=${m.score} (real probe)`,
|
| 1099 |
+
});
|
| 1100 |
+
}
|
| 1101 |
+
return hits;
|
| 1102 |
+
}
|
| 1103 |
+
// Substring match — collect from up to 3 closest tokens
|
| 1104 |
+
const subMatches = idx.tokens.filter(t => t.includes(q) || q.includes(t)).slice(0, 3);
|
| 1105 |
+
for (const tok of subMatches) {
|
| 1106 |
+
for (const m of (idx.matches[tok] || []).slice(0, 4)) { // top-4 per substring hit
|
| 1107 |
+
hits.push({
|
| 1108 |
+
layerNorm: m.layer / (idx.num_layers - 1),
|
| 1109 |
+
fi: m.feature,
|
| 1110 |
+
label: `${tok} L${m.layer}/F${m.feature} score=${m.score}`,
|
| 1111 |
+
});
|
| 1112 |
+
}
|
| 1113 |
+
}
|
| 1114 |
+
return hits.length > 0 ? hits : null;
|
| 1115 |
+
}
|
| 1116 |
+
|
| 1117 |
+
// Demo dataset — small handcrafted index used when real index isn't deployed yet.
|
| 1118 |
+
// `layerNorm` is a fraction of total depth (0.0 = first layer, 1.0 = last). At runtime each
|
| 1119 |
+
// match is mapped to the model's actual layer range (round(layerNorm * (L-1))).
|
| 1120 |
+
// The Paris→capital match is sourced from the LarQL Gate-3 result on Gemma 4 E2B (feature
|
| 1121 |
+
// 11179 at layer 27 of 36 → normalized depth 27/35 ≈ 0.77). Other matches are illustrative:
|
| 1122 |
+
// they sit in plausible layer bands (entity zone 0.45–0.55, prediction zone 0.90+) and use
|
| 1123 |
+
// representative feature indices visible in the viewer's 128-feature spiral.
|
| 1124 |
+
const SEARCH_INDEX = {
|
| 1125 |
+
'gemma-4-e2b': {
|
| 1126 |
+
'paris': [{ layerNorm: 0.77, fi: 11, label: 'L27/F11179 — capital-of-X concept (Gate-3)' },
|
| 1127 |
+
{ layerNorm: 0.62, fi: 8, label: 'Eiffel/landmark co-activation' }],
|
| 1128 |
+
'capital': [{ layerNorm: 0.77, fi: 11, label: 'capital-of-X concept' },
|
| 1129 |
+
{ layerNorm: 0.40, fi: 19, label: 'country routing' }],
|
| 1130 |
+
'eiffel': [{ layerNorm: 0.62, fi: 8 }],
|
| 1131 |
+
'france': [{ layerNorm: 0.40, fi: 19 }, { layerNorm: 0.55, fi: 22 }],
|
| 1132 |
+
'python': [{ layerNorm: 0.50, fi: 27, label: 'code-block routing' },
|
| 1133 |
+
{ layerNorm: 0.92, fi: 3, label: 'def/class token preference' }],
|
| 1134 |
+
'code': [{ layerNorm: 0.50, fi: 27 }, { layerNorm: 0.30, fi: 33 }],
|
| 1135 |
+
'doctor': [{ layerNorm: 0.50, fi: 41, label: 'medical entity cluster' }],
|
| 1136 |
+
'einstein': [{ layerNorm: 0.50, fi: 47, label: 'physicist entity' }],
|
| 1137 |
+
},
|
| 1138 |
+
'qwen3-8b': {
|
| 1139 |
+
'paris': [{ layerNorm: 0.55, fi: 11 }],
|
| 1140 |
+
'capital': [{ layerNorm: 0.55, fi: 11 }, { layerNorm: 0.42, fi: 19 }],
|
| 1141 |
+
'python': [{ layerNorm: 0.50, fi: 27 }],
|
| 1142 |
+
'code': [{ layerNorm: 0.50, fi: 27 }],
|
| 1143 |
+
},
|
| 1144 |
+
'llama-3.1-8b': {
|
| 1145 |
+
'paris': [{ layerNorm: 0.65, fi: 11 }],
|
| 1146 |
+
'capital': [{ layerNorm: 0.65, fi: 11 }],
|
| 1147 |
+
},
|
| 1148 |
+
// Bonsai/BitNet 1-bit models: dissolved — no concentrated features to highlight
|
| 1149 |
+
'bonsai-1bit': { /* intentionally empty: dissolution means no feature locality */ },
|
| 1150 |
+
};
|
| 1151 |
+
|
| 1152 |
+
let savedColors = null; // Map<key, [Color, Color, ...]> — original instance colors per group
|
| 1153 |
+
let highlightedFeatureIdxs = []; // [{ key, globalIdx }]
|
| 1154 |
+
let searchHaloMeshes = []; // glowing ring meshes added per match
|
| 1155 |
+
|
| 1156 |
+
function _saveColorsIfNeeded() {
|
| 1157 |
+
if (savedColors) return;
|
| 1158 |
+
savedColors = new Map();
|
| 1159 |
+
for (const key of activeKeys) {
|
| 1160 |
+
const g = groups[key];
|
| 1161 |
+
if (!g || !g.instanceMesh.instanceColor) continue;
|
| 1162 |
+
const arr = g.instanceMesh.instanceColor.array;
|
| 1163 |
+
savedColors.set(key, new Float32Array(arr)); // copy
|
| 1164 |
+
}
|
| 1165 |
+
}
|
| 1166 |
+
|
| 1167 |
+
function clearSearch() {
|
| 1168 |
+
// Restore original colors
|
| 1169 |
+
if (savedColors) {
|
| 1170 |
+
for (const key of activeKeys) {
|
| 1171 |
+
const g = groups[key];
|
| 1172 |
+
const original = savedColors.get(key);
|
| 1173 |
+
if (!g || !original) continue;
|
| 1174 |
+
g.instanceMesh.instanceColor.array.set(original);
|
| 1175 |
+
g.instanceMesh.instanceColor.needsUpdate = true;
|
| 1176 |
+
}
|
| 1177 |
+
savedColors = null;
|
| 1178 |
+
}
|
| 1179 |
+
// Reset scales of previously-highlighted features
|
| 1180 |
+
for (const h of highlightedFeatureIdxs) {
|
| 1181 |
+
const g = groups[h.key];
|
| 1182 |
+
if (!g) continue;
|
| 1183 |
+
const m = new THREE.Matrix4();
|
| 1184 |
+
g.instanceMesh.getMatrixAt(h.globalIdx, m);
|
| 1185 |
+
const pos = new THREE.Vector3(), q = new THREE.Quaternion(), s = new THREE.Vector3();
|
| 1186 |
+
m.decompose(pos, q, s);
|
| 1187 |
+
s.setScalar(1.0);
|
| 1188 |
+
m.compose(pos, q, s);
|
| 1189 |
+
g.instanceMesh.setMatrixAt(h.globalIdx, m);
|
| 1190 |
+
}
|
| 1191 |
+
for (const key of activeKeys) {
|
| 1192 |
+
const g = groups[key];
|
| 1193 |
+
if (g) g.instanceMesh.instanceMatrix.needsUpdate = true;
|
| 1194 |
+
}
|
| 1195 |
+
highlightedFeatureIdxs = [];
|
| 1196 |
+
// Remove halo meshes
|
| 1197 |
+
for (const mesh of searchHaloMeshes) {
|
| 1198 |
+
scene.remove(mesh);
|
| 1199 |
+
mesh.geometry.dispose();
|
| 1200 |
+
mesh.material.dispose();
|
| 1201 |
+
}
|
| 1202 |
+
searchHaloMeshes = [];
|
| 1203 |
+
document.getElementById('search-status').textContent = '';
|
| 1204 |
+
}
|
| 1205 |
+
|
| 1206 |
+
function highlightSearch(query) {
|
| 1207 |
+
const q = (query || '').trim().toLowerCase();
|
| 1208 |
+
clearSearch();
|
| 1209 |
+
if (!q) return;
|
| 1210 |
+
|
| 1211 |
+
let totalMatches = 0;
|
| 1212 |
+
const matchInfos = [];
|
| 1213 |
+
_saveColorsIfNeeded();
|
| 1214 |
+
|
| 1215 |
+
const HIGHLIGHT_COLOR = new THREE.Color(0xffffff); // pure white pops against everything
|
| 1216 |
+
const PULSE_SCALE = 6.0;
|
| 1217 |
+
|
| 1218 |
+
for (const key of activeKeys) {
|
| 1219 |
+
// Try real index first, then fall back to demo dataset
|
| 1220 |
+
let matches = lookupReal(key, q);
|
| 1221 |
+
if (!matches) {
|
| 1222 |
+
const idx = SEARCH_INDEX[key] || {};
|
| 1223 |
+
matches = idx[q] || [];
|
| 1224 |
+
if (matches.length === 0) {
|
| 1225 |
+
for (const k of Object.keys(idx)) {
|
| 1226 |
+
if (k.includes(q) || q.includes(k)) matches = matches.concat(idx[k]);
|
| 1227 |
+
}
|
| 1228 |
+
}
|
| 1229 |
+
}
|
| 1230 |
+
if (matches.length === 0) continue;
|
| 1231 |
+
|
| 1232 |
+
const g = groups[key];
|
| 1233 |
+
if (!g) continue;
|
| 1234 |
+
const L = VINDEX_DATA[key].layers;
|
| 1235 |
+
const F = VINDEX_DATA[key].F;
|
| 1236 |
+
const offsetX = g.group.position.x;
|
| 1237 |
+
|
| 1238 |
+
for (const m of matches) {
|
| 1239 |
+
const li = Math.round(m.layerNorm * (L - 1));
|
| 1240 |
+
const fi = Math.min(Math.max(m.fi, 0), F - 1);
|
| 1241 |
+
const globalIdx = li * F + fi;
|
| 1242 |
+
if (globalIdx < 0 || globalIdx >= g.featureMeta.length) continue;
|
| 1243 |
+
g.instanceMesh.setColorAt(globalIdx, HIGHLIGHT_COLOR);
|
| 1244 |
+
// Scale up
|
| 1245 |
+
const matrix = new THREE.Matrix4();
|
| 1246 |
+
g.instanceMesh.getMatrixAt(globalIdx, matrix);
|
| 1247 |
+
const pos = new THREE.Vector3(), quat = new THREE.Quaternion(), scl = new THREE.Vector3();
|
| 1248 |
+
matrix.decompose(pos, quat, scl);
|
| 1249 |
+
scl.setScalar(PULSE_SCALE);
|
| 1250 |
+
matrix.compose(pos, quat, scl);
|
| 1251 |
+
g.instanceMesh.setMatrixAt(globalIdx, matrix);
|
| 1252 |
+
highlightedFeatureIdxs.push({ key, globalIdx });
|
| 1253 |
+
|
| 1254 |
+
// Add a glowing halo ring at the feature's world position so it's findable at any zoom
|
| 1255 |
+
const meta = g.featureMeta[globalIdx];
|
| 1256 |
+
const haloGeo = new THREE.RingGeometry(0.55, 0.85, 32);
|
| 1257 |
+
const haloMat = new THREE.MeshBasicMaterial({
|
| 1258 |
+
color: 0xffe060, transparent: true, opacity: 0.92, side: THREE.DoubleSide, depthWrite: false,
|
| 1259 |
+
});
|
| 1260 |
+
const halo = new THREE.Mesh(haloGeo, haloMat);
|
| 1261 |
+
halo.position.set(meta.x + offsetX, meta.y, meta.z);
|
| 1262 |
+
halo.lookAt(camera.position);
|
| 1263 |
+
halo.userData.isSearchHalo = true;
|
| 1264 |
+
halo.userData.featurePos = halo.position.clone();
|
| 1265 |
+
scene.add(halo);
|
| 1266 |
+
searchHaloMeshes.push(halo);
|
| 1267 |
+
|
| 1268 |
+
matchInfos.push(`${key} L${li}/F${fi}${m.label ? ` (${m.label.slice(0, 40)})` : ''}`);
|
| 1269 |
+
totalMatches++;
|
| 1270 |
+
}
|
| 1271 |
+
g.instanceMesh.instanceColor.needsUpdate = true;
|
| 1272 |
+
g.instanceMesh.instanceMatrix.needsUpdate = true;
|
| 1273 |
+
}
|
| 1274 |
+
|
| 1275 |
+
const status = document.getElementById('search-status');
|
| 1276 |
+
if (totalMatches === 0) {
|
| 1277 |
+
status.textContent = `no matches for "${q}"`;
|
| 1278 |
+
} else {
|
| 1279 |
+
status.textContent = `${totalMatches} match${totalMatches === 1 ? '' : 'es'}: ${matchInfos.slice(0, 3).join(', ')}${matchInfos.length > 3 ? '…' : ''}`;
|
| 1280 |
+
}
|
| 1281 |
+
}
|
| 1282 |
+
|
| 1283 |
+
// ── UI wiring ─────────────────────────────────────────────────
|
| 1284 |
+
const modelSelect = document.getElementById('model-select');
|
| 1285 |
+
const compareBtn = document.getElementById('compare-btn');
|
| 1286 |
+
const demoBtn = document.getElementById('demo-btn');
|
| 1287 |
+
const viewBtn = document.getElementById('view-btn');
|
| 1288 |
+
const searchInput = document.getElementById('search-input');
|
| 1289 |
+
const tier2Btn = document.getElementById('tier2-btn');
|
| 1290 |
+
|
| 1291 |
+
let _searchTimer = null;
|
| 1292 |
+
searchInput.addEventListener('input', e => {
|
| 1293 |
+
clearTimeout(_searchTimer);
|
| 1294 |
+
const v = e.target.value;
|
| 1295 |
+
_searchTimer = setTimeout(() => highlightSearch(v), 180);
|
| 1296 |
+
});
|
| 1297 |
+
searchInput.addEventListener('keydown', e => {
|
| 1298 |
+
if (e.key === 'Escape') { searchInput.value = ''; clearSearch(); }
|
| 1299 |
+
});
|
| 1300 |
+
|
| 1301 |
+
modelSelect.addEventListener('change', () => {
|
| 1302 |
+
if (demoRunning) return;
|
| 1303 |
+
// Drop stale highlight state — the InstancedMesh references are about to be disposed
|
| 1304 |
+
savedColors = null;
|
| 1305 |
+
highlightedFeatureIdxs = [];
|
| 1306 |
+
compareMode ? showCompare(modelSelect.value, 'bonsai-1bit') : showSingle(modelSelect.value);
|
| 1307 |
+
// Lazy-load the real search index for this model (no-op if already loaded or unavailable)
|
| 1308 |
+
loadRealSearchIndex(modelSelect.value);
|
| 1309 |
+
// Re-apply current search query against the new model
|
| 1310 |
+
if (searchInput.value.trim()) {
|
| 1311 |
+
setTimeout(() => highlightSearch(searchInput.value), 100);
|
| 1312 |
+
} else {
|
| 1313 |
+
document.getElementById('search-status').textContent = '';
|
| 1314 |
+
}
|
| 1315 |
+
});
|
| 1316 |
+
compareBtn.addEventListener('click', () => {
|
| 1317 |
+
if (demoRunning) return;
|
| 1318 |
+
compareMode ? showSingle(modelSelect.value) : showCompare(modelSelect.value, 'bonsai-1bit');
|
| 1319 |
+
});
|
| 1320 |
+
demoBtn.addEventListener('click', () => {
|
| 1321 |
+
if (demoRunning) {
|
| 1322 |
+
demoRunning = false; controls.enabled = true;
|
| 1323 |
+
demoBtn.textContent = '▶ Demo'; setDemoText('');
|
| 1324 |
+
showSingle(modelSelect.value);
|
| 1325 |
+
} else { runDemo(); }
|
| 1326 |
+
});
|
| 1327 |
+
viewBtn.addEventListener('click', () => {
|
| 1328 |
+
if (demoRunning) return;
|
| 1329 |
+
viewMode = (viewMode === 'cylinder') ? 'flat' : 'cylinder';
|
| 1330 |
+
viewBtn.textContent = (viewMode === 'flat') ? '🌀 3D View' : '🔌 2D Circuit';
|
| 1331 |
+
// Reposition camera + reconfigure controls for the new view
|
| 1332 |
+
if (viewMode === 'flat') {
|
| 1333 |
+
camera.position.set(0, 0, 28);
|
| 1334 |
+
controls.target.set(0, 0, 0);
|
| 1335 |
+
// Lock the orbit to a near-flat plane (allow only small tilt + pan + zoom)
|
| 1336 |
+
controls.minPolarAngle = Math.PI / 2 - 0.05;
|
| 1337 |
+
controls.maxPolarAngle = Math.PI / 2 + 0.05;
|
| 1338 |
+
controls.enableRotate = false;
|
| 1339 |
+
} else {
|
| 1340 |
+
camera.position.set(0, 4, 26);
|
| 1341 |
+
controls.target.set(0, 0, 0);
|
| 1342 |
+
controls.minPolarAngle = 0;
|
| 1343 |
+
controls.maxPolarAngle = Math.PI;
|
| 1344 |
+
controls.enableRotate = true;
|
| 1345 |
+
}
|
| 1346 |
+
controls.update();
|
| 1347 |
+
// Rebuild active groups with new layout
|
| 1348 |
+
if (compareMode) {
|
| 1349 |
+
showCompare(modelSelect.value, 'bonsai-1bit');
|
| 1350 |
+
} else {
|
| 1351 |
+
showSingle(modelSelect.value);
|
| 1352 |
+
}
|
| 1353 |
+
});
|
| 1354 |
+
tier2Btn.addEventListener('click', () => {
|
| 1355 |
+
tier2Open = !tier2Open;
|
| 1356 |
+
document.getElementById('tier2-metrics').className = tier2Open ? 'open' : '';
|
| 1357 |
+
tier2Btn.textContent = tier2Open ? 'METRICS ▴' : 'METRICS ▾';
|
| 1358 |
+
});
|
| 1359 |
+
|
| 1360 |
+
// ── Resize ────────────────────────────────────────────────────
|
| 1361 |
+
window.addEventListener('resize', () => {
|
| 1362 |
+
camera.aspect = innerWidth / innerHeight;
|
| 1363 |
+
camera.updateProjectionMatrix();
|
| 1364 |
+
renderer.setSize(innerWidth, innerHeight);
|
| 1365 |
+
composer.setSize(innerWidth, innerHeight);
|
| 1366 |
+
bloom.resolution.set(innerWidth, innerHeight);
|
| 1367 |
+
});
|
| 1368 |
+
|
| 1369 |
+
// ── Animate ───────────────────────────────────────────────────
|
| 1370 |
+
let t = 0;
|
| 1371 |
+
function animate() {
|
| 1372 |
+
requestAnimationFrame(animate);
|
| 1373 |
+
t += 0.003;
|
| 1374 |
+
if (!demoRunning) {
|
| 1375 |
+
for (const key of activeKeys) {
|
| 1376 |
+
const g = groups[key];
|
| 1377 |
+
if (g) g.group.rotation.y = Math.sin(t * 0.18) * 0.05;
|
| 1378 |
+
}
|
| 1379 |
+
}
|
| 1380 |
+
// Modulate bloom for "alive" feel
|
| 1381 |
+
bloom.strength = 0.7 + Math.sin(t * 0.6) * 0.08;
|
| 1382 |
+
// Billboard search halos to face the camera + pulse opacity
|
| 1383 |
+
if (searchHaloMeshes.length > 0) {
|
| 1384 |
+
const pulse = 0.65 + Math.abs(Math.sin(t * 4.0)) * 0.32;
|
| 1385 |
+
for (const halo of searchHaloMeshes) {
|
| 1386 |
+
halo.lookAt(camera.position);
|
| 1387 |
+
halo.material.opacity = pulse;
|
| 1388 |
+
}
|
| 1389 |
+
}
|
| 1390 |
+
controls.update();
|
| 1391 |
+
composer.render();
|
| 1392 |
+
}
|
| 1393 |
+
|
| 1394 |
+
// ── Init ──────────────────────────────────────────────────────
|
| 1395 |
+
showSingle('qwen3-8b');
|
| 1396 |
+
animate();
|
| 1397 |
+
// Try to fetch the real search index for the default model (silent if 404)
|
| 1398 |
+
loadRealSearchIndex('qwen3-8b');
|
| 1399 |
+
// URL param handling: ?view=flat → 2D circuit, ?autoplay → demo, ?model=… → switch model, ?q=paris → search
|
| 1400 |
+
const _params = new URLSearchParams(location.search);
|
| 1401 |
+
const _modelParam = _params.get('model');
|
| 1402 |
+
if (_modelParam && VINDEX_DATA[_modelParam]) {
|
| 1403 |
+
modelSelect.value = _modelParam;
|
| 1404 |
+
modelSelect.dispatchEvent(new Event('change'));
|
| 1405 |
+
}
|
| 1406 |
+
if (_params.get('view') === 'flat') {
|
| 1407 |
+
viewBtn.click();
|
| 1408 |
+
}
|
| 1409 |
+
if (_params.has('autoplay')) {
|
| 1410 |
+
setTimeout(runDemo, 1200);
|
| 1411 |
+
}
|
| 1412 |
+
const _q = _params.get('q');
|
| 1413 |
+
if (_q) {
|
| 1414 |
+
searchInput.value = _q;
|
| 1415 |
+
setTimeout(() => highlightSearch(_q), 1100); // wait for buildGroup + loadRealSearchIndex
|
| 1416 |
+
}
|
| 1417 |
+
</script>
|
| 1418 |
+
</body>
|
| 1419 |
+
</html>
|
search-indexes/google-gemma-4-e2b-it.bge.f16.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:667c3d1d6be119733475935b18f41e61fbc27ce8ebab81819f56c3a0ad5e45bf
|
| 3 |
+
size 3840000
|
search-indexes/google-gemma-4-e2b-it.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
search-indexes/google-gemma-4-e2b-it.meta.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_id": "google/gemma-4-E2B-it",
|
| 3 |
+
"n_tokens": 5000,
|
| 4 |
+
"top_k": 8,
|
| 5 |
+
"has_bge": true,
|
| 6 |
+
"build_unix_ts": 1776826346
|
| 7 |
+
}
|
vindex-hero-bg.gif
ADDED
|
Git LFS Details
|