{"spec_id":"network-bipartite","library":"echarts","language":"javascript","code":"// anyplot.ai\n// network-bipartite: Bipartite Network Graph\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-09-05\n\nconst theme = window.ANYPLOT_THEME;\nconst t = window.ANYPLOT_TOKENS;\nconst muted = theme === \"dark\" ? \"#A8A79F\" : \"#6B6A63\";\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Author-paper affiliation network: which researchers contributed to which\n// publications. Edge weight = number of shared authorship credits.\nconst researchers = [\n  \"A. Chen\", \"B. Diallo\", \"C. Kowalski\", \"D. Nakamura\", \"E. Osei\",\n  \"F. Petrova\", \"G. Reyes\", \"H. Singh\", \"I. Tanaka\", \"J. Volkov\",\n];\nconst papers = [\n  \"Graph Embeddings\", \"Federated Learning\", \"Attention Mechanisms\",\n  \"Robotic Grasping\", \"Climate Modeling\", \"Protein Folding\",\n  \"Speech Synthesis\", \"Autonomous Driving\", \"Drug Discovery\",\n  \"Quantum Computing\", \"Computer Vision\", \"Natural Language\",\n  \"Recommender Systems\", \"Time Series Forecasting\",\n];\n\n// [researcherIndex, paperIndex, weight]\nconst links = [\n  [0, 0, 3], [0, 1, 2], [0, 3, 4], [0, 5, 1], [0, 13, 1],\n  [1, 0, 2], [1, 2, 3],\n  [2, 1, 4], [2, 4, 2], [2, 6, 3],\n  [3, 3, 1], [3, 7, 2], [3, 8, 3], [3, 9, 1],\n  [4, 2, 2], [4, 5, 3],\n  [5, 6, 4], [5, 10, 2], [5, 11, 1],\n  [6, 4, 3], [6, 9, 2],\n  [7, 8, 2], [7, 10, 3], [7, 12, 1],\n  [8, 7, 1], [8, 13, 4],\n  [9, 11, 2], [9, 12, 3], [9, 13, 2],\n];\n\n// --- Layout: two fixed columns, degree-weighted node size -------------------\nconst researcherDegree = researchers.map(\n  (_, i) => links.filter((l) => l[0] === i).length,\n);\nconst paperDegree = papers.map(\n  (_, j) => links.filter((l) => l[1] === j).length,\n);\n\n// Crossing minimization: barycenter heuristic, alternating a few sweeps\n// between the two columns so each side settles near the average position\n// of its connected neighbors on the other side.\nconst researcherLinks = researchers.map((_, i) =>\n  links.filter((l) => l[0] === i).map((l) => l[1]),\n);\nconst paperLinks = papers.map((_, j) =>\n  links.filter((l) => l[1] === j).map((l) => l[0]),\n);\n\nconst barycenterSort = (order, neighborLists, otherOrder) => {\n  const otherRank = new Map(otherOrder.map((idx, pos) => [idx, pos]));\n  return order\n    .map((idx, pos) => {\n      const neighbors = neighborLists[idx];\n      const avg = neighbors.length\n        ? neighbors.reduce((sum, n) => sum + otherRank.get(n), 0) /\n          neighbors.length\n        : pos;\n      return { idx, avg };\n    })\n    .sort((a, b) => a.avg - b.avg)\n    .map((e) => e.idx);\n};\n\nlet researcherOrder = researchers.map((_, i) => i);\nlet paperOrder = papers.map((_, j) => j);\nfor (let sweep = 0; sweep < 8; sweep++) {\n  paperOrder = barycenterSort(paperOrder, paperLinks, researcherOrder);\n  researcherOrder = barycenterSort(researcherOrder, researcherLinks, paperOrder);\n}\nconst researcherPos = new Map(researcherOrder.map((idx, pos) => [idx, pos]));\nconst paperPos = new Map(paperOrder.map((idx, pos) => [idx, pos]));\n\nconst yFor = (i, n) => (n === 1 ? 0.5 : i / (n - 1));\nconst sizeFor = (degree) => 16 + degree * 6;\n\nconst nodes = [\n  ...researchers.map((name, i) => ({\n    id: `r${i}`,\n    name,\n    category: 0,\n    x: 0,\n    y: yFor(researcherPos.get(i), researchers.length),\n    symbolSize: sizeFor(researcherDegree[i]),\n    label: { position: \"left\" },\n  })),\n  ...papers.map((name, j) => ({\n    id: `p${j}`,\n    name,\n    category: 1,\n    x: 1,\n    y: yFor(paperPos.get(j), papers.length),\n    symbolSize: sizeFor(paperDegree[j]),\n    label: { position: \"right\" },\n  })),\n];\n\nconst maxWeight = Math.max(...links.map((l) => l[2]));\nconst edges = links.map(([r, p, weight]) => ({\n  source: `r${r}`,\n  target: `p${p}`,\n  value: weight,\n  lineStyle: {\n    color: weight === maxWeight ? t.amber : muted,\n    width: 1 + (weight / maxWeight) * 4,\n    opacity: 0.35 + (weight / maxWeight) * 0.3,\n    curveness: 0.08,\n  },\n  emphasis: {\n    lineStyle: { opacity: 1, width: 2 + (weight / maxWeight) * 4 },\n    label: { show: true, formatter: \"{c}\", color: t.ink, fontSize: 12 },\n  },\n}));\n\n// --- Init ---------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"network-bipartite · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n  },\n  legend: {\n    data: [\"Researchers\", \"Papers\"],\n    top: 78,\n    left: \"center\",\n    itemGap: 32,\n    textStyle: { color: t.inkSoft, fontSize: 15 },\n  },\n  series: [\n    {\n      type: \"graph\",\n      layout: \"none\",\n      preserveAspect: \"contain\",\n      roam: false,\n      left: \"16%\",\n      right: \"16%\",\n      top: \"16%\",\n      bottom: \"8%\",\n      symbol: \"circle\",\n      categories: [\n        { name: \"Researchers\", itemStyle: { color: t.palette[0] } },\n        { name: \"Papers\", itemStyle: { color: t.palette[1] } },\n      ],\n      label: {\n        show: true,\n        color: t.inkSoft,\n        fontSize: 14,\n        distance: 10,\n      },\n      itemStyle: { borderColor: t.pageBg, borderWidth: 2 },\n      emphasis: { focus: \"adjacency\", scale: false, lineStyle: { opacity: 1 } },\n      blur: { itemStyle: { opacity: 0.25 }, lineStyle: { opacity: 0.1 } },\n      data: nodes,\n      links: edges,\n    },\n  ],\n});\n"}