HRM / puzzle_visualizer.html
imone's picture
Release
bd62227
raw
history blame
14.1 kB
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ARC‐Converted Dataset Visualizer (Upload Local Folder)</title>
<style>
body {
font-family: sans-serif;
margin: 16px;
}
.selector-area {
margin-bottom: 1rem;
}
.grid-canvas {
margin: 4px;
border: 1px solid #ccc;
}
.example-container {
display: inline-block;
margin: 0 16px 16px 0;
vertical-align: top;
}
.puzzle-display {
margin-top: 1rem;
}
.puzzle-id {
font-weight: bold;
margin-bottom: 0.5rem;
}
#groupList, #puzzleList {
margin: 1rem 0;
}
.group-item, .puzzle-item {
cursor: pointer;
margin: 4px 8px 4px 0;
padding: 2px 6px;
border: 1px solid #aaa;
display: inline-block;
}
.group-item:hover, .puzzle-item:hover {
background: #eef;
}
</style>
</head>
<body>
<h1>ARC‐Converted Dataset Visualizer (Local Directory)</h1>
<div class="selector-area">
<!-- 1) Directory input with webkitdirectory, mozdirectory -->
<label>Upload ARC Folder:</label>
<input type="file" id="folderInput"
webkitdirectory mozdirectory multiple
onchange="onFolderSelected(event)" />
<br><br>
<!-- 2) We'll enable set/subset selection after user chooses a folder and data is validated -->
<label>Set:</label>
<select id="setSelect" disabled>
<option value="train">train</option>
<option value="test">test</option>
</select>
<label> Subset:</label>
<select id="subsetSelect" disabled>
<option value="all">all</option>
</select>
<button id="loadBtn" disabled>Load</button>
</div>
<div>
<div id="groupList"></div>
<div id="puzzleList"></div>
<div class="puzzle-display" id="puzzleView"></div>
</div>
<!--
3) Use local 'assets/npyjs.js' from your project folder instead of a CDN.
Make sure 'assets/npyjs.js' is the unbundled or UMD version that doesn't
contain "import" statements.
-->
<script src="assets/npyjs.js"></script>
<script>
/***************************************************************************
* Global Maps & Variables
***************************************************************************/
// Map from "train/all__inputs.npy" => File, etc.
let filesByPath = {};
// Once loaded, we store typed arrays for the chosen set/subset
let inputsArr, labelsArr;
let puzzleIndicesArr, groupIndicesArr, puzzleIdentifiersArr;
let identifiersJson;
// The shape of inputs is [N_examples, seqLen], so we discover seqLen & gridSize
let seqLen = 0;
let gridSize = 0;
/***************************************************************************
* 1) Handle folder selection: read all files, find identifiers.json,
* remove topmost folder from each file path, validate.
***************************************************************************/
function onFolderSelected(event) {
filesByPath = {};
const fileList = event.target.files;
if (!fileList || fileList.length === 0) {
alert("No files selected!");
return;
}
// We'll gather all webkitRelativePaths
const paths = [];
for (let i = 0; i < fileList.length; i++) {
// Typically "arc-aug-10/train/all__inputs.npy", etc.
const file = fileList[i];
const relPath = file.webkitRelativePath || file.mozRelativePath || file.name;
paths.push(relPath);
}
// 1. Check if we have "identifiers.json" somewhere.
const idPath = paths.find(p => p.endsWith("identifiers.json"));
if (!idPath) {
alert("Error: No 'identifiers.json' found in the uploaded folder.");
return;
}
// 2. Derive the top-level directory from that file's path
// e.g. if idPath = "arc-aug-10/identifiers.json", topDir = "arc-aug-10"
// If there's no slash, topDir = "" => do nothing
let topDir = "";
const lastSlash = idPath.lastIndexOf("/");
if (lastSlash >= 0) {
topDir = idPath.substring(0, lastSlash);
}
// 3. Rebuild filesByPath with the top folder removed.
// For example, if topDir = "arc-aug-10", then "arc-aug-10/train/all__inputs.npy"
// becomes "train/all__inputs.npy"
for (let i = 0; i < fileList.length; i++) {
const file = fileList[i];
let relPath = file.webkitRelativePath || file.mozRelativePath || file.name;
// If relPath starts with "arc-aug-10/", remove that prefix
if (topDir && relPath.startsWith(topDir + "/")) {
relPath = relPath.substring(topDir.length + 1);
}
filesByPath[relPath] = file;
}
// Enable set/subset selection and "Load"
document.getElementById("setSelect").disabled = false;
document.getElementById("subsetSelect").disabled = false;
document.getElementById("loadBtn").disabled = false;
}
// When user clicks "Load," parse the .npy for the chosen set/subset
document.getElementById("loadBtn").addEventListener("click", async () => {
document.getElementById("groupList").innerHTML = "";
document.getElementById("puzzleList").innerHTML = "";
document.getElementById("puzzleView").innerHTML = "";
const setName = document.getElementById("setSelect").value; // e.g. "train"
const subsetName = document.getElementById("subsetSelect").value; // e.g. "all"
try {
await loadDataset(setName, subsetName);
buildGroupList(); // show groups
} catch (err) {
console.error(err);
alert("Error while loading dataset: " + err);
}
});
/***************************************************************************
* 2) Load .npy from local files using Npyjs + FileReader (ArrayBuffer)
***************************************************************************/
async function loadDataset(setName, subsetName) {
const prefix = `${setName}/${subsetName}__`;
// e.g. "train/all__inputs.npy"
const inputsPath = prefix + "inputs.npy";
const labelsPath = prefix + "labels.npy";
const pIdxPath = prefix + "puzzle_indices.npy";
const gIdxPath = prefix + "group_indices.npy";
const pIdsPath = prefix + "puzzle_identifiers.npy";
const identifiersPath = "identifiers.json";
// Check existence
const needed = [inputsPath, labelsPath, pIdxPath, gIdxPath, pIdsPath, identifiersPath];
for (const f of needed) {
if (!filesByPath[f]) {
throw new Error(`Missing file: ${f}`);
}
}
// parseNpy => read from File -> ArrayBuffer -> Npyjs => typed array
const inputsNpy = await parseNpy(filesByPath[inputsPath]);
const labelsNpy = await parseNpy(filesByPath[labelsPath]);
const puzzleIndicesNpy= await parseNpy(filesByPath[pIdxPath]);
const groupIndicesNpy = await parseNpy(filesByPath[gIdxPath]);
const puzzleIdsNpy = await parseNpy(filesByPath[pIdsPath]);
inputsArr = inputsNpy.data;
labelsArr = labelsNpy.data;
puzzleIndicesArr = puzzleIndicesNpy.data;
groupIndicesArr = groupIndicesNpy.data;
puzzleIdentifiersArr = puzzleIdsNpy.data;
// shape e.g. [N_examples, seqLen]
seqLen = inputsNpy.shape[1];
gridSize = Math.sqrt(seqLen);
// read JSON
identifiersJson = await readJsonFile(filesByPath[identifiersPath]);
}
/***************************************************************************
* parseNpy => read a File as ArrayBuffer, parse with npyjs
***************************************************************************/
function parseNpy(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async () => {
try {
const arrayBuffer = reader.result;
const npy = new npyjs();
resolve(await npy.parse(arrayBuffer));
} catch (err) {
reject(err);
}
};
reader.onerror = err => reject(err);
reader.readAsArrayBuffer(file);
});
}
/***************************************************************************
* readJsonFile => read a local JSON file into object
***************************************************************************/
function readJsonFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
try {
const obj = JSON.parse(reader.result);
resolve(obj);
} catch (err) {
reject(err);
}
};
reader.onerror = (err) => reject(err);
reader.readAsText(file);
});
}
/***************************************************************************
* 3) Build group list in UI
***************************************************************************/
function buildGroupList() {
document.getElementById("groupList").innerHTML = "<h3>Groups</h3>";
const groupListDiv = document.getElementById("groupList");
const nGroups = groupIndicesArr.length - 1;
for (let g = 0; g < nGroups; g++) {
const div = document.createElement("span");
div.className = "group-item";
div.textContent = `Group ${g}`;
div.onclick = () => onSelectGroup(g);
groupListDiv.appendChild(div);
}
}
/***************************************************************************
* onSelectGroup => show puzzles in that group
***************************************************************************/
function onSelectGroup(groupIndex) {
document.getElementById("puzzleList").innerHTML = "";
document.getElementById("puzzleView").innerHTML = "";
const puzzleListDiv = document.getElementById("puzzleList");
puzzleListDiv.innerHTML = `<h4>Puzzles in Group ${groupIndex}</h4>`;
const firstPuzzle = groupIndicesArr[groupIndex];
const lastPuzzle = groupIndicesArr[groupIndex + 1];
for (let p = firstPuzzle; p < lastPuzzle; p++) {
const puzzleIntId = puzzleIdentifiersArr[p];
const puzzleStrId = (puzzleIntId < identifiersJson.length)
? identifiersJson[puzzleIntId]
: "<unknown>";
const div = document.createElement("span");
div.className = "puzzle-item";
div.textContent = `Puzzle #${p} [ID=${puzzleIntId}: ${puzzleStrId}]`;
div.onclick = () => onSelectPuzzle(p);
puzzleListDiv.appendChild(div);
}
}
/***************************************************************************
* onSelectPuzzle => show each example
***************************************************************************/
function onSelectPuzzle(puzzleIndex) {
const puzzleView = document.getElementById("puzzleView");
puzzleView.innerHTML = "";
// puzzle ID
const puzzleIntId = puzzleIdentifiersArr[puzzleIndex];
const puzzleStrId = (puzzleIntId < identifiersJson.length)
? identifiersJson[puzzleIntId]
: "<unknown>";
const titleDiv = document.createElement("div");
titleDiv.className = "puzzle-id";
titleDiv.textContent = `Puzzle #${puzzleIndex} — ID: ${puzzleStrId}`;
puzzleView.appendChild(titleDiv);
// Examples are [puzzleIndicesArr[p], puzzleIndicesArr[p+1])
const firstExample = puzzleIndicesArr[puzzleIndex];
const lastExample = puzzleIndicesArr[puzzleIndex + 1];
for (let e = firstExample; e < lastExample; e++) {
const inputSeq = slice1D(inputsArr, e*seqLen, (e+1)*seqLen);
const outputSeq = slice1D(labelsArr, e*seqLen, (e+1)*seqLen);
const inputGrid = decodeGrid(inputSeq);
const outputGrid = decodeGrid(outputSeq);
const exDiv = document.createElement("div");
exDiv.className = "example-container";
exDiv.appendChild(document.createTextNode(`Example ${e}`));
exDiv.appendChild(document.createElement("br"));
exDiv.appendChild(renderGrid(inputGrid));
exDiv.appendChild(renderGrid(outputGrid));
puzzleView.appendChild(exDiv);
}
}
/***************************************************************************
* slice1D => typed array slicing
***************************************************************************/
function slice1D(arr, start, end) {
const result = new Uint32Array(end - start);
for (let i = start; i < end; i++) {
result[i - start] = Number(arr[i]);
}
return result;
}
/***************************************************************************
* decodeGrid => turn the flattened seq of length=gridSize^2 into 2D
***************************************************************************/
function decodeGrid(seq) {
const grid = [];
let idx = 0;
for (let r = 0; r < gridSize; r++) {
const row = [];
for (let c = 0; c < gridSize; c++) {
row.push(seq[idx]);
idx++;
}
grid.push(row);
}
return grid;
}
/***************************************************************************
* renderGrid => draws a 2D grid to <canvas>
***************************************************************************/
function renderGrid(grid2d) {
const rows = grid2d.length;
const cols = grid2d[0].length;
const scale = 10;
const canvas = document.createElement("canvas");
canvas.width = cols * scale;
canvas.height = rows * scale;
canvas.className = "grid-canvas";
const ctx = canvas.getContext("2d");
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const val = grid2d[r][c];
ctx.fillStyle = indexToColor(val);
ctx.fillRect(c * scale, r * scale, scale, scale);
}
}
return canvas;
}
/***************************************************************************
* indexToColor => color palette:
* 0 => pad => white
* 1 => eos => light gray
* 2..11 => original color(0..9)
***************************************************************************/
function indexToColor(value) {
if (value === 0) return "#FFFFFF"; // pad => white
if (value === 1) return "#DDDDDD"; // eos => light gray
// shift by 2 => original color in [0..9]
const colorIdx = value - 2;
const palette = [
"#000000", // color0 => black
"#FF0000", // color1 => red
"#00FF00", // color2 => green
"#0000FF", // color3 => blue
"#FFFF00", // color4 => yellow
"#FFA500", // color5 => orange
"#800080", // color6 => purple
"#00FFFF", // color7 => cyan
"#FFC0CB", // color8 => pink
"#808080" // color9 => gray
];
if (colorIdx >= 0 && colorIdx < palette.length) {
return palette[colorIdx];
}
return "#FFFFFF"; // fallback
}
</script>
</body>
</html>