PageSourceSearch

https://edekeulenaar.github.io/ai-alignment-actors/figures2.js?v=20261002m1

js edekeulenaar.github.io collected 2026-10-03 09:07:35 UTC 20,731 bytes, 426 lines download raw bytes

1/* Three figures added in September 2026, built from the document corpus itself:
2   A. the discourse peculiar to each document type (keyness.json)
3   B. the citation network between documents (citations.json)
4   C. the actors named in each document type and what they do (actors_by_type.json)
5   Each figure renders only if its container is present on the page. */
6(function () {
7  "use strict";
8
9  var PALETTE = ["#3f6fb5", "#c2632c", "#4a8a67", "#8b5a9e", "#b0843a", "#5b8ca8",
10                 "#a8555f", "#6b7a4a", "#8a6a55", "#7a7a8c", "#457f8a", "#9c6b3f"];
11  var colourOf = function (values) {
12    var scale = {};
13    values.forEach(function (v, i) { scale[v] = PALETTE[i % PALETTE.length]; });
14    return function (v) { return scale[v] || "#8a8a8a"; };
15  };
16  var el = function (id) { return document.getElementById(id); };
17  var clear = function (node) { while (node.firstChild) node.removeChild(node.firstChild); };
18
19  function typeSelector(container, types, onChange) {
20    var bar = document.createElement("div");
21    bar.className = "fig2-types";
22    types.forEach(function (t, i) {
23      var b = document.createElement("button");
24      b.type = "button";
25      b.textContent = t;
26      b.className = "fig2-type" + (i === 0 ? " is-on" : "");
27      b.addEventListener("click", function () {
28        bar.querySelectorAll(".fig2-type").forEach(function (o) { o.classList.remove("is-on"); });
29        b.classList.add("is-on");
30        onChange(t);
31      });
32      bar.appendChild(b);
33    });
34    container.appendChild(bar);
35  }
36
37  /* ── A. Discourse of each document type ─────────────────────────────────── */
38  function keyness(data) {
39    var host = el("fig-keyness");
40    if (!host) return;
41    var types = Object.keys(data).sort(function (a, b) {
42      return data[b][0].category_docs - data[a][0].category_docs;
43    });
44    var chart = document.createElement("div");
45    chart.className = "fig2-chart";
46    typeSelector(host, types, draw);
47    host.appendChild(chart);
48
49    function draw(type) {
50      clear(chart);
51      var terms = data[type].slice(0, 18);
52      var max = d3.max(terms, function (d) { return d.z; });
53      var note = document.createElement("p");
54      note.className = "fig2-note";
55      note.textContent = terms[0].category_docs + " documents of this type. Candidate concepts are the "
56        + "noun chunks of each document, so verbs and bare adjectives are excluded; each is scored by the "
57        + "log-odds ratio of its document frequency in this type against the rest of the corpus, with an "
58        + "informative Dirichlet prior (Monroe, Colaresi and Quinn 2008), which keeps rare concepts and "
59        + "small types comparable. A concept counts only if more than one company uses it. Bars show that "
60        + "score; the figure on the right is the number of documents of this type naming the concept.";
61      chart.appendChild(note);
62      var list = document.createElement("ul");
63      list.className = "fig2-bars";
64      terms.forEach(function (t) {
65        var li = document.createElement("li");
66        var label = document.createElement("span");
67        label.className = "fig2-term";
68        label.textContent = t.term;
69        var track = document.createElement("span");
70        track.className = "fig2-track";
71        var bar = document.createElement("span");
72        bar.className = "fig2-bar";
73        bar.style.width = Math.max(2, (t.z / max) * 100) + "%";
74        bar.style.background = PALETTE[0];
75        track.appendChild(bar);
76        var value = document.createElement("span");
77        value.className = "fig2-value";
78        value.textContent = t.docs + "/" + t.category_docs + " docs";
79        li.appendChild(label); li.appendChild(track); li.appendChild(value);
80        list.appendChild(li);
81      });
82      chart.appendChild(list);
83    }
84    draw(types[0]);
85  }
86
87  /* ── B. Citation network ────────────────────────────────────────────────── */
88  function network(data) {
89    var host = el("fig-network");
90    if (!host) return;
91    var companies = Array.from(new Set(data.nodes.map(function (n) { return n.company; }))).sort();
92    var categories = Array.from(new Set(data.nodes.map(function (n) { return n.category; }))).sort();
93    var colour = colourOf(categories);
94    var state = {company: "All companies", category: null};
95    var applyHighlight = function () {};
96
97    var controls = document.createElement("div");
98    controls.className = "fig2-types";
99    ["All companies"].concat(companies).forEach(function (c, i) {
100      var b = document.createElement("button");
101      b.type = "button"; b.textContent = c;
102      b.className = "fig2-type" + (i === 0 ? " is-on" : "");
103      b.addEventListener("click", function () {
104        controls.querySelectorAll(".fig2-type").forEach(function (o) { o.classList.remove("is-on"); });
105        b.classList.add("is-on"); state.company = c; draw();
106      });
107      controls.appendChild(b);
108    });
109    host.appendChild(controls);
110
111    var legend = document.createElement("div");
112    legend.className = "fig2-legend";
113    categories.forEach(function (c) {
114      var s = document.createElement("button");
115      s.type = "button";
116      s.className = "fig2-key fig2-key-btn";
117      s.innerHTML = '<i style="background:' + colour(c) + '"></i>' + c;
118      s.addEventListener("click", function () {
119        state.category = state.category === c ? null : c;
120        legend.querySelectorAll(".fig2-key-btn").forEach(function (o) {
121          o.classList.toggle("is-on", o === s && state.category === c);
122        });
123        applyHighlight();
124      });
125      legend.appendChild(s);
126    });
127    host.appendChild(legend);
128
129    var wrap = document.createElement("div");
130    wrap.className = "fig2-canvas";
131    host.appendChild(wrap);
132    var tip = document.createElement("div");
133    tip.className = "fig2-tip"; tip.hidden = true;
134    host.appendChild(tip);
135
136    function draw() {
137      clear(wrap);
138      var nodes = data.nodes.filter(function (n) {
139        return state.company === "All companies" || n.company === state.company;
140      }).map(function (n) { return Object.assign({}, n); });
141      var keep = new Set(nodes.map(function (n) { return n.key; }));
142      var links = data.edges.filter(function (e) {
143        return keep.has(e.source) && keep.has(e.target);
144      }).map(function (e) { return {source: e.source, target: e.target, type: e.type}; });
145      // Keep the picture readable: drop documents that end up with no visible tie.
146      var linked = new Set();
147      links.forEach(function (l) { linked.add(l.source); linked.add(l.target); });
148      nodes = nodes.filter(function (n) { return linked.has(n.key); });
149
150      var width = wrap.clientWidth || 900, height = 560;
151      var svg = d3.select(wrap).append("svg")
152        .attr("viewBox", "0 0 " + width + " " + height)
153        .attr("width", "100%").attr("height", height);
154      var g = svg.append("g");
155      svg.call(d3.zoom().scaleExtent([0.3, 4]).on("zoom", function (ev) {
156        g.attr("transform", ev.transform);
157      }));
158
159      var r = function (n) { return 3 + Math.sqrt(n.cited_by) * 2.4; };
160      var sim = d3.forceSimulation(nodes)
161        .force("link", d3.forceLink(links).id(function (d) { return d.key; }).distance(60).strength(0.25))
162        .force("charge", d3.forceManyBody().strength(-90))
163        .force("centre", d3.forceCenter(width / 2, height / 2))
164        .force("collide", d3.forceCollide().radius(function (d) { return r(d) + 3; }));
165
166      var link = g.append("g").attr("stroke", "#c9c4bb").attr("stroke-opacity", 0.55)
167        .selectAll("line").data(links).join("line")
168        .attr("stroke-width", function (d) { return d.type === "title" ? 1.4 : 0.7; })
169        .attr("stroke-dasharray", function (d) { return d.type === "title" ? null : "2,2"; });
170
171      var node = g.append("g").selectAll("circle").data(nodes).join("circle")
172        .attr("r", r)
173        .attr("fill", function (d) { return colour(d.category); })
174        .attr("stroke", "#fff").attr("stroke-width", 0.8)
175        .style("cursor", "pointer")
176        .on("mousemove", function (ev, d) {
177          tip.hidden = false;
178          tip.innerHTML = "<strong>" + d.title + "</strong><br>" + d.company + " — " + d.category
179            + "<br>cited by " + d.cited_by + ", cites " + d.cites;
180          var box = host.getBoundingClientRect();
181          tip.style.left = (ev.clientX - box.left + 12) + "px";
182          tip.style.top = (ev.clientY - box.top + 12) + "px";
183        })
184        .on("mouseleave", function () { tip.hidden = true; })
185        .on("click", function (ev, d) { if (d.url) window.open(d.url, "_blank", "noopener"); })
186        .call(d3.drag()
187          .on("start", function (ev, d) { if (!ev.active) sim.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; })
188          .on("drag", function (ev, d) { d.fx = ev.x; d.fy = ev.y; })
189          .on("end", function (ev, d) { if (!ev.active) sim.alphaTarget(0); d.fx = null; d.fy = null; }));
190
191      var labelled = nodes.slice().sort(function (a, b) { return b.cited_by - a.cited_by; }).slice(0, 12);
192      var label = g.append("g").selectAll("text").data(labelled).join("text")
193        .text(function (d) { return d.title.length > 42 ? d.title.slice(0, 40) + "…" : d.title; })
194        .attr("font-size", 10).attr("fill", "#2b2b2b").attr("pointer-events", "none")
195        .attr("paint-order", "stroke").attr("stroke", "#fdfcf9").attr("stroke-width", 3);
196
197      applyHighlight = function () {
198        var on = state.category;
199        node.attr("opacity", function (d) { return !on || d.category === on ? 1 : 0.12; })
200            .attr("stroke", function (d) { return on && d.category === on ? "#23211e" : "#fff"; })
201            .attr("stroke-width", function (d) { return on && d.category === on ? 1.4 : 0.8; });
202        link.attr("stroke-opacity", function (d) {
203          if (!on) return 0.55;
204          return d.source.category === on || d.target.category === on ? 0.5 : 0.05;
205        });
206        label.attr("opacity", function (d) { return !on || d.category === on ? 1 : 0.15; });
207      };
208      applyHighlight();
209
210      function ticked() {
211        link.attr("x1", function (d) { return d.source.x; }).attr("y1", function (d) { return d.source.y; })
212            .attr("x2", function (d) { return d.target.x; }).attr("y2", function (d) { return d.target.y; });
213        node.attr("cx", function (d) { return d.x; }).attr("cy", function (d) { return d.y; });
214        label.attr("x", function (d) { return d.x + r(d) + 3; }).attr("y", function (d) { return d.y + 3; });
215      }
216      sim.on("tick", ticked);
217      // Inside a slide of the presentation the browser runs animations slowly, so the layout
218      // is computed at once and shown settled.
219      if (new URLSearchParams(location.search).has("embed")) {
220        sim.stop();
221        for (var t = 0; t < 400; t++) sim.tick();
222        ticked();
223      }
224
225      var count = document.createElement("p");
226      count.className = "fig2-note";
227      count.textContent = nodes.length + " documents and " + links.length
228        + " ties. A solid line means one document names another by title; a dotted line means it links to it. "
229        + "Circles grow with the number of documents citing them. Click a circle to open the document.";
230      wrap.appendChild(count);
231    }
232    draw();
233  }
234
235  /* ── C. Actors per document type ────────────────────────────────────────── */
236  function actors(data) {
237    var host = el("fig-actors");
238    if (!host) return;
239    var types = Object.keys(data).sort(function (a, b) {
240      return d3.sum(data[b], function (d) { return d.documents; })
241           - d3.sum(data[a], function (d) { return d.documents; });
242    });
243    var relations = Array.from(new Set([].concat.apply([], types.map(function (t) {
244      return data[t].map(function (a) { return a.main_relation; });
245    }
245)))).sort();
246    var colour = colourOf(relations);
247
248    var picked = null;
249    var legend = document.createElement("div");
250    legend.className = "fig2-legend";
251    relations.forEach(function (rel) {
252      var s = document.createElement("button");
253      s.type = "button";
254      s.className = "fig2-key fig2-key-btn";
255      s.innerHTML = '<i style="background:' + colour(rel) + '"></i>' + rel;
256      s.addEventListener("click", function () {
257        picked = picked === rel ? null : rel;
258        legend.querySelectorAll(".fig2-key-btn").forEach(function (o) {
259          o.classList.toggle("is-on", o === s && picked === rel);
260        });
261        d3.select(chart).selectAll("g rect")
262          .attr("opacity", function (d) { return !picked || d.data.main_relation === picked ? 1 : 0.12; });
263        d3.select(chart).selectAll("g text")
264          .attr("opacity", function (d) { return !picked || d.data.main_relation === picked ? 1 : 0.12; });
265      });
266      legend.appendChild(s);
267    });
268
269    var chart = document.createElement("div");
270    chart.className = "fig2-chart";
271    typeSelector(host, types, draw);
272    host.appendChild(legend);
273    host.appendChild(chart);
274
275    function draw(type) {
276      clear(chart);
277      picked = null;
278      legend.querySelectorAll(".fig2-key-btn").forEach(function (o) { o.classList.remove("is-on"); });
279      var items = data[type].slice(0, 24);
280      var width = chart.clientWidth || 900, height = 420;
281      var root = d3.hierarchy({children: items}).sum(function (d) { return d.documents; })
282        .sort(function (a, b) { return b.value - a.value; });
283      d3.treemap().size([width, height]).paddingInner(3).round(true)(root);
284      var svg = d3.select(chart).append("svg")
285        .attr("viewBox", "0 0 " + width + " " + height)
286        .attr("width", "100%").attr("height", height);
287      var cell = svg.selectAll("g").data(root.leaves()).join("g")
288        .attr("transform", function (d) { return "translate(" + d.x0 + "," + d.y0 + ")"; });
289      cell.append("rect")
290        .attr("width", function (d) { return d.x1 - d.x0; })
291        .attr("height", function (d) { return d.y1 - d.y0; })
292        .attr("fill", function (d) { return colour(d.data.main_relation); })
293        .attr("rx", 2);
294      cell.append("title").text(function (d) {
295        return d.data.actor + " — " + d.data.main_relation + "\n" + d.data.documents
296          + " documents of this type";
297      });
298      cell.append("text").attr("x", 5).attr("y", 14)
299        .attr("font-size", 11).attr("fill", "#fff")
300        .text(function (d) {
301          var space = d.x1 - d.x0;
302          if (space < 46 || d.y1 - d.y0 < 18) return "";
303          var chars = Math.floor(space / 6.2);
304          return d.data.actor.length > chars ? d.data.actor.slice(0, chars - 1) + "…" : d.data.actor;
305        });
306      cell.append("text").attr("x", 5).attr("y", 27)
307        .attr("font-size", 9.5).attr("fill", "rgba(255,255,255,.85)")
308        .text(function (d) {
309          return (d.x1 - d.x0) < 80 || (d.y1 - d.y0) < 34 ? "" : d.data.documents + " docs";
310        });
311      var note = document.createElement("p");
312      note.className = "fig2-note";
313      note.textContent = "Each square is an actor credited in documents of this type; its size is the number "
314        + "of such documents, and its colour the role those documents most often give it: a source of the "
315        + "definition of a conduct or risk, conceiving a training method, taking part in training, creating a "
316        + "benchmark, external evaluation or red teaming. Actors and roles come from the second, "
317        + "retrieval-grounded reading, and each was found verbatim on its page in the index; an actor merely "
318        + "named in a document, without one of these roles, is not counted. The authoring company is not "
319        + "counted in its own documents. Click a role in the legend to highlight it.";
320      chart.appendChild(note);
321    }
322    draw(types[0]);
323  }
324
325  /* ── B2. Actor types across document types ──────────────────────────────── */
326  function actorTypes(data) {
327    var host = el("fig-actor-types");
328    if (!host) return;
329    var max = 0;
330    data.types.forEach(function (t) {
331      data.categories.forEach(function (c) { max = Math.max(max, data.matrix[t][c]); });
332    });
333
334    var tip = document.createElement("div");
335    tip.className = "fig2-tip";
336    tip.hidden = true;
337    host.appendChild(tip);
338
339    var table = document.createElement("table");
340    table.className = "fig2-matrix";
341    var head = document.createElement("tr");
342    head.appendChild(document.createElement("th"));
343    data.categories.forEach(function (c, i) {
344      var th = document.createElement("th");
345      th.dataset.col = String(i);
346      var span = document.createElement("span");
347      span.textContent = c;
348      th.appendChild(span);
349      head.appendChild(th);
350    });
351    table.appendChild(head);
352
353    function show(ev, cell) {
354      var t = cell.dataset.type, c = cell.dataset.category, n = cell.dataset.count;
355      var who = ((data.examples[t] || {})[c] || []);
356      tip.hidden = false;
357      tip.innerHTML = "<strong>" + t + "</strong> in <strong>" + c + "</strong><br>"
358        + n + (n === "1" ? " document" : " documents") + " of this type name an actor of this type"
359        + (who.length ? "<br><span class=\"fig2-tip-who\">" + who.join(", ")
360                        + (who.length >= 6 ? " …" : "") + "</span>" : "");
361      var box = host.getBoundingClientRect();
362      var x = ev.clientX - box.left + 14, y = ev.clientY - box.top + 14;
363      tip.style.left = Math.min(x, host.clientWidth - tip.offsetWidth - 8) + "px";
364      tip.style.top = y + "px";
365    }
366    function highlight(row, col, on) {
367      table.querySelectorAll('[data-row="' + row + '"], [data-col="' + col + '"]')
368        .forEach(function (e) { e.classList.toggle("is-lit", on); });
369    }
370
371    data.types.forEach(function (t, r) {
372      var tr = document.createElement("tr");
373      var label = document.createElement("th");
374      label.className = "fig2-matrix-row";
375      label.dataset.row = String(r);
376      label.innerHTML = '<i style="background:' + (data.colors[t] || "#b9b1a2") + '"></i>' + t;
377      tr.appendChild(label);
378      data.categories.forEach(function (c, i) {
379        var n = data.matrix[t][c];
380        var td = document.createElement("td");
381        td.dataset.row = String(r);
382        td.dataset.col = String(i);
383        if (n) {
384          td.className = "has-value";
385          td.dataset.type = t;
386          td.dataset.category = c;
387          td.dataset.count = String(n);
388          td.style.background = data.colors[t] || "#6f6a62";
389          // Square root keeps the smaller counts visible next to the internal actors.
390          td.style.opacity = String(0.14 + 0.86 * Math.sqrt(n) / Math.sqrt(max));
391          td.textContent = n;
392          td.addEventListener("mousemove", function (ev) { show(ev, td); });
393          td.addEventListener("mouseenter", function () { highlight(r, i, true); });
394          td.addEventListener("mouseleave", function () {
395            highlight(r, i, false); tip.hidden = true;
396          });
397        }
398        tr.appendChild(td);
399      });
400      table.appendChild(tr);
401    });
402    host.appendChild(table);
403
404    var note = document.createElement("p");
405    note.className = "fig2-note";
406    note.textContent = "Rows are actor types, columns document types. A cell counts the documents of "
407      + "that type in which an actor of that type is credited with one of four roles: a source of the "
408      + "definition of a conduct or risk, a part in training (conceiving the method or taking part), the "
409      + "creation of a benchmark, or the evaluation or red teaming of a model. Every actor and every role was "
410      + "checked against its page in the index, and each actor's type against the corpus and, where needed, "
411      + "the web. The authoring company is not counted in its own documents. Colour follows the actor type, "
412      + "its strength the count (square-root scale). Hover a cell for the actors behind it.";
413    host.appendChild(note);
414  }
415
416  function load(file, fn) {
417    fetch(file + "?v=20260918").then(function (r) { return r.json(); }).then(fn)
418      .catch(function (e) { console.warn("figures2: could not load " + file, e); });
419  }
420  document.addEventListener("DOMContentLoaded", function () {
421    if (el("fig-keyness")) load("keyness.json", keyness);
422    if (el("fig-network")) load("citations.json", network);
423    if (el("fig-actors")) load("actors_by_type.json", actors);
424    if (el("fig-actor-types")) load("actor_types_by_category.json", actorTypes);
425  });
426})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.