{"spec_id":"parallel-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// parallel-basic: Basic Parallel Coordinates Plot\n// Library: echarts 6.1.0 | JavaScript 22.23.1\n// Quality: 89/100 | Created: 2026-07-24\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: ML hyperparameter search runs, grouped by optimizer -------------\n// Tiny fixed-seed LCG — the browser has no seeded RNG.\nfunction lcg(seed) {\n  let state = seed >>> 0;\n  return function () {\n    state = (state * 1664525 + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\nconst rand = lcg(42);\n\nconst OPTIMIZERS = [\"Adam\", \"RMSprop\", \"SGD\"];\nconst OPT_BONUS = { Adam: 4.5, RMSprop: 2, SGD: 0 };\nconst BATCH_SIZES = [16, 32, 64, 128, 256];\nconst RUNS_PER_OPTIMIZER = 16;\n\nfunction clamp(v, lo, hi) {\n  return Math.max(lo, Math.min(hi, v));\n}\n\nconst runsByOptimizer = OPTIMIZERS.map((optimizer) => {\n  const rows = [];\n  for (let i = 0; i < RUNS_PER_OPTIMIZER; i++) {\n    const learningRate = Math.pow(10, -4 + rand() * 3); // 1e-4 .. 1e-1, log-uniform\n    const batchSize = BATCH_SIZES[Math.floor(rand() * BATCH_SIZES.length)];\n    const dropout = rand() * 0.5; // 0 .. 0.5\n    const hiddenUnits = 32 + rand() * 468; // 32 .. 500\n\n    const lrPenalty = Math.abs(Math.log10(learningRate) - -2.5) * 5;\n    const dropoutPenalty = Math.abs(dropout - 0.25) * 8;\n    const hiddenBonus = (hiddenUnits / 500) * 5;\n    const batchPenalty = (Math.abs(batchSize - 64) / 256) * 3;\n    const noise = (rand() - 0.5) * 6;\n\n    const valAccuracy = clamp(\n      75 +\n        OPT_BONUS[optimizer] -\n        lrPenalty -\n        dropoutPenalty +\n        hiddenBonus -\n        batchPenalty +\n        noise,\n      55,\n      99,\n    );\n\n    rows.push([learningRate, batchSize, dropout, hiddenUnits, valAccuracy]);\n  }\n  return rows;\n});\n\n// --- Chart -------------------------------------------------------------------\nconst axisNameStyle = { color: t.ink, fontSize: 15 };\nconst axisLabelStyle = { color: t.inkSoft, fontSize: 13 };\nconst axisLineStyle = { lineStyle: { color: t.inkSoft } };\n\nconst chart = echarts.init(document.getElementById(\"container\"));\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"parallel-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 32,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: \"500\" },\n  },\n  legend: {\n    top: 92,\n    data: OPTIMIZERS,\n    textStyle: { color: t.ink, fontSize: 16 },\n    itemGap: 28,\n    itemWidth: 26,\n    itemHeight: 4,\n  },\n  parallelAxis: [\n    {\n      dim: 0,\n      name: \"Learning Rate\",\n      type: \"log\",\n      min: 1e-4,\n      max: 1e-1,\n      nameTextStyle: axisNameStyle,\n      axisLabel: { ...axisLabelStyle, formatter: (v) => v.toExponential(0) },\n      axisLine: axisLineStyle,\n    },\n    {\n      dim: 1,\n      name: \"Batch Size\",\n      min: 0,\n      max: 280,\n      nameTextStyle: axisNameStyle,\n      axisLabel: axisLabelStyle,\n      axisLine: axisLineStyle,\n    },\n    {\n      dim: 2,\n      name: \"Dropout Rate\",\n      min: 0,\n      max: 0.5,\n      nameTextStyle: axisNameStyle,\n      axisLabel: axisLabelStyle,\n      axisLine: axisLineStyle,\n    },\n    {\n      dim: 3,\n      name: \"Hidden Units\",\n      min: 0,\n      max: 500,\n      nameTextStyle: axisNameStyle,\n      axisLabel: axisLabelStyle,\n      axisLine: axisLineStyle,\n    },\n    {\n      dim: 4,\n      name: \"Val Accuracy (%)\",\n      min: 55,\n      max: 100,\n      nameTextStyle: axisNameStyle,\n      axisLabel: axisLabelStyle,\n      axisLine: axisLineStyle,\n    },\n  ],\n  parallel: {\n    left: 110,\n    right: 110,\n    top: 190,\n    bottom: 120,\n    parallelAxisDefault: {\n      axisLine: axisLineStyle,\n      axisTick: { lineStyle: { color: t.inkSoft } },\n      splitLine: { show: false },\n    },\n  },\n  tooltip: {\n    backgroundColor: t.elevatedBg,\n    borderColor: t.grid,\n    textStyle: { color: t.ink, fontSize: 14 },\n  },\n  // Draw back-to-front (SGD, RMSprop, Adam) so Adam's accuracy edge renders on\n  // top of the crossing mesh instead of being buried by later series; colors\n  // stay pinned to each optimizer's canonical Imprint slot regardless of draw order.\n  series: [\"SGD\", \"RMSprop\", \"Adam\"].map((optimizer) => {\n    const idx = OPTIMIZERS.indexOf(optimizer);\n    const isAdam = optimizer === \"Adam\";\n    return {\n      name: optimizer,\n      type: \"parallel\",\n      smooth: false,\n      itemStyle: { color: t.palette[idx] },\n      lineStyle: {\n        color: t.palette[idx],\n        width: isAdam ? 2.2 : 1.5,\n        opacity: isAdam ? 0.6 : 0.32,\n      },\n      emphasis: { lineStyle: { opacity: 0.9, width: 2.8 } },\n      data: runsByOptimizer[idx],\n    };\n  }),\n});\n"}