open-interpretability 0.0.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- open_interpretability-0.0.20.dist-info/METADATA +157 -0
- open_interpretability-0.0.20.dist-info/RECORD +256 -0
- open_interpretability-0.0.20.dist-info/WHEEL +4 -0
- open_interpretability-0.0.20.dist-info/entry_points.txt +2 -0
- open_interpretability-0.0.20.dist-info/licenses/LICENSE +21 -0
- openinterp/__init__.py +6 -0
- openinterp/agents/__init__.py +25 -0
- openinterp/agents/base.py +229 -0
- openinterp/agents/judge.py +149 -0
- openinterp/agents/proposer.py +471 -0
- openinterp/agents/proposer_profiles.py +79 -0
- openinterp/agents/review_measurements.py +173 -0
- openinterp/agents/reviewer.py +498 -0
- openinterp/agreement.py +63 -0
- openinterp/audit_options.py +79 -0
- openinterp/baseline.py +254 -0
- openinterp/benchmark.py +450 -0
- openinterp/blends.py +239 -0
- openinterp/builder/__init__.py +6 -0
- openinterp/builder/agents.py +260 -0
- openinterp/builder/aligned.py +41 -0
- openinterp/builder/charts.py +411 -0
- openinterp/builder/circuit_pages.py +37 -0
- openinterp/builder/circuits.py +264 -0
- openinterp/builder/compiler.py +495 -0
- openinterp/builder/corpora.py +98 -0
- openinterp/builder/coverage.py +46 -0
- openinterp/builder/dashboard.py +255 -0
- openinterp/builder/disk_memo.py +65 -0
- openinterp/builder/experiment.py +105 -0
- openinterp/builder/heat.py +121 -0
- openinterp/builder/hrefs.py +71 -0
- openinterp/builder/learned_baseline.py +204 -0
- openinterp/builder/model_facts.py +138 -0
- openinterp/builder/neuronpedia_export.py +107 -0
- openinterp/builder/parallel.py +100 -0
- openinterp/builder/parquet_export.py +458 -0
- openinterp/builder/pieces.py +127 -0
- openinterp/builder/pii.py +211 -0
- openinterp/builder/registry_stats.py +144 -0
- openinterp/builder/research.py +109 -0
- openinterp/builder/sections.py +28 -0
- openinterp/builder/significance.py +103 -0
- openinterp/builder/sister_graph.py +180 -0
- openinterp/builder/standing.py +185 -0
- openinterp/builder/statuses.py +34 -0
- openinterp/builder/stopped.py +91 -0
- openinterp/builder/stops.py +30 -0
- openinterp/builder/superpositions.py +102 -0
- openinterp/builder/templates/_agent_role_cells.html +9 -0
- openinterp/builder/templates/_causal_chart.html +67 -0
- openinterp/builder/templates/_circuit.html +8 -0
- openinterp/builder/templates/_dashboard.html +133 -0
- openinterp/builder/templates/_experiment.html +500 -0
- openinterp/builder/templates/_macros.html +82 -0
- openinterp/builder/templates/_patchscope.html +33 -0
- openinterp/builder/templates/_prior_art.html +24 -0
- openinterp/builder/templates/_specificity.html +21 -0
- openinterp/builder/templates/_steering.html +34 -0
- openinterp/builder/templates/_superposition.html +62 -0
- openinterp/builder/templates/_vocab_chart.html +88 -0
- openinterp/builder/templates/_wiring.html +44 -0
- openinterp/builder/templates/about.html +95 -0
- openinterp/builder/templates/agent.html +69 -0
- openinterp/builder/templates/agents.html +80 -0
- openinterp/builder/templates/base.html +103 -0
- openinterp/builder/templates/circuit.html +64 -0
- openinterp/builder/templates/circuits.html +46 -0
- openinterp/builder/templates/experiment.html +21 -0
- openinterp/builder/templates/hub.html +16 -0
- openinterp/builder/templates/index.html +387 -0
- openinterp/builder/templates/layer.html +69 -0
- openinterp/builder/templates/learned-baseline.html +44 -0
- openinterp/builder/templates/model.html +151 -0
- openinterp/builder/templates/models.html +47 -0
- openinterp/builder/templates/neuron.html +234 -0
- openinterp/builder/templates/paper.html +39 -0
- openinterp/builder/templates/redirect.html +1 -0
- openinterp/builder/templates/research.html +31 -0
- openinterp/builder/templates/user.html +54 -0
- openinterp/builder/templates/users.html +39 -0
- openinterp/builder/templates/wiki/experiment.md +271 -0
- openinterp/builder/templates/wiki/index.md +32 -0
- openinterp/builder/templates/wiki/neuron.md +30 -0
- openinterp/builder/templates/wiki/stop.md +36 -0
- openinterp/builder/tree.py +470 -0
- openinterp/builder/users.py +142 -0
- openinterp/builder/wiki.py +427 -0
- openinterp/checks.py +429 -0
- openinterp/cli.py +494 -0
- openinterp/cli_baseline.py +332 -0
- openinterp/cli_benchmark.py +300 -0
- openinterp/cli_build.py +128 -0
- openinterp/cli_check.py +51 -0
- openinterp/cli_concepts.py +76 -0
- openinterp/cli_corpus.py +252 -0
- openinterp/cli_costs.py +257 -0
- openinterp/cli_coverage.py +76 -0
- openinterp/cli_crossref.py +86 -0
- openinterp/cli_errata.py +142 -0
- openinterp/cli_evidence.py +495 -0
- openinterp/cli_evidence_store.py +93 -0
- openinterp/cli_explain.py +101 -0
- openinterp/cli_loop.py +125 -0
- openinterp/cli_memory.py +28 -0
- openinterp/cli_neuronpedia.py +175 -0
- openinterp/cli_pause.py +45 -0
- openinterp/cli_pole_effects.py +133 -0
- openinterp/cli_probing.py +119 -0
- openinterp/cli_registry.py +348 -0
- openinterp/cli_reproduce.py +446 -0
- openinterp/cli_review.py +302 -0
- openinterp/cli_scan.py +223 -0
- openinterp/cli_score.py +446 -0
- openinterp/cli_sisters.py +86 -0
- openinterp/cli_submission.py +217 -0
- openinterp/cli_superpositions.py +118 -0
- openinterp/cli_wiring.py +54 -0
- openinterp/codeid.py +73 -0
- openinterp/concepts.py +400 -0
- openinterp/core/__init__.py +59 -0
- openinterp/core/agent_names.py +76 -0
- openinterp/core/attribution.py +101 -0
- openinterp/core/cache_dirs.py +15 -0
- openinterp/core/causal.py +82 -0
- openinterp/core/confounds.py +103 -0
- openinterp/core/contributors.py +26 -0
- openinterp/core/corpora.py +126 -0
- openinterp/core/costs.py +430 -0
- openinterp/core/draws.py +109 -0
- openinterp/core/errata.py +244 -0
- openinterp/core/evidence.py +160 -0
- openinterp/core/examples.py +93 -0
- openinterp/core/exceptions.py +29 -0
- openinterp/core/explanation.py +130 -0
- openinterp/core/fence.py +42 -0
- openinterp/core/heavy_lock.py +114 -0
- openinterp/core/identifiers.py +198 -0
- openinterp/core/isolation.py +149 -0
- openinterp/core/ledger.py +130 -0
- openinterp/core/licenses.py +28 -0
- openinterp/core/manifold.py +117 -0
- openinterp/core/partners.py +32 -0
- openinterp/core/patchscope.py +59 -0
- openinterp/core/paths.py +40 -0
- openinterp/core/pause.py +96 -0
- openinterp/core/pricing.py +178 -0
- openinterp/core/prior_art.py +44 -0
- openinterp/core/probing.py +146 -0
- openinterp/core/profiles.py +221 -0
- openinterp/core/project.py +28 -0
- openinterp/core/run_environment.py +38 -0
- openinterp/core/schemas.py +484 -0
- openinterp/core/specificity.py +49 -0
- openinterp/core/stats.py +172 -0
- openinterp/core/steering.py +49 -0
- openinterp/core/strict.py +19 -0
- openinterp/core/takedowns.py +136 -0
- openinterp/core/transcripts.py +130 -0
- openinterp/core/wiring.py +80 -0
- openinterp/corpus.py +476 -0
- openinterp/corpus_cache.py +145 -0
- openinterp/corpus_layouts.py +108 -0
- openinterp/corpus_presets.py +168 -0
- openinterp/corpus_rebuild.py +93 -0
- openinterp/coverage.py +155 -0
- openinterp/crossref.py +442 -0
- openinterp/data/__init__.py +9 -0
- openinterp/data/offensive_words.txt +58 -0
- openinterp/data/public_suffix_list.dat +15771 -0
- openinterp/data/sample_corpus.txt +40 -0
- openinterp/draw_steps.py +359 -0
- openinterp/errata_checks.py +129 -0
- openinterp/evidence.py +462 -0
- openinterp/evidence_branch.py +119 -0
- openinterp/evidence_lenses.py +75 -0
- openinterp/experiment_loop.py +357 -0
- openinterp/explain.py +252 -0
- openinterp/half_precision.py +79 -0
- openinterp/harness/__init__.py +37 -0
- openinterp/harness/ane/__init__.py +9 -0
- openinterp/harness/ane/backend.py +324 -0
- openinterp/harness/ane/cache.py +276 -0
- openinterp/harness/ane/chunks.py +163 -0
- openinterp/harness/ane/convert.py +116 -0
- openinterp/harness/ane/forward.py +258 -0
- openinterp/harness/ane/qualified.py +40 -0
- openinterp/harness/annotations.py +329 -0
- openinterp/harness/attribution.py +96 -0
- openinterp/harness/causal.py +264 -0
- openinterp/harness/confounds.py +184 -0
- openinterp/harness/covariance.py +301 -0
- openinterp/harness/crossref.py +195 -0
- openinterp/harness/cuda_devices.py +88 -0
- openinterp/harness/dead.py +124 -0
- openinterp/harness/devices.py +401 -0
- openinterp/harness/dictionaries.py +496 -0
- openinterp/harness/dreaming.py +116 -0
- openinterp/harness/environment.py +64 -0
- openinterp/harness/explanation_scores.py +371 -0
- openinterp/harness/features.py +276 -0
- openinterp/harness/first_position.py +137 -0
- openinterp/harness/heads.py +226 -0
- openinterp/harness/hooks.py +499 -0
- openinterp/harness/input_attribution.py +367 -0
- openinterp/harness/jlens.py +183 -0
- openinterp/harness/layer_cache.py +301 -0
- openinterp/harness/layer_major.py +274 -0
- openinterp/harness/lookahead.py +93 -0
- openinterp/harness/manifold.py +200 -0
- openinterp/harness/memory.py +471 -0
- openinterp/harness/moe.py +134 -0
- openinterp/harness/patcher.py +322 -0
- openinterp/harness/patchscope.py +177 -0
- openinterp/harness/probing.py +383 -0
- openinterp/harness/scan.py +462 -0
- openinterp/harness/scan_backend.py +201 -0
- openinterp/harness/sister_passes.py +463 -0
- openinterp/harness/sisters.py +363 -0
- openinterp/harness/sites.py +101 -0
- openinterp/harness/specificity.py +107 -0
- openinterp/harness/steering.py +159 -0
- openinterp/harness/superpositions.py +116 -0
- openinterp/harness/units.py +203 -0
- openinterp/harness/wiring.py +432 -0
- openinterp/harness/wiring_activations.py +285 -0
- openinterp/langid.py +73 -0
- openinterp/names.py +129 -0
- openinterp/neuronpedia_baseline.py +88 -0
- openinterp/neuronpedia_review.py +148 -0
- openinterp/planner.py +466 -0
- openinterp/planner_effects.py +89 -0
- openinterp/planner_heads.py +92 -0
- openinterp/planner_models.py +174 -0
- openinterp/planner_sisters.py +122 -0
- openinterp/planner_states.py +57 -0
- openinterp/planner_superpositions.py +114 -0
- openinterp/pole_effects.py +102 -0
- openinterp/prior_art.py +143 -0
- openinterp/reproduce.py +450 -0
- openinterp/revision.py +45 -0
- openinterp/runner_cache.py +35 -0
- openinterp/score_controls.py +169 -0
- openinterp/sensitive.py +243 -0
- openinterp/show.py +236 -0
- openinterp/signing.py +209 -0
- openinterp/sisters.py +476 -0
- openinterp/superposition_finders.py +225 -0
- openinterp/sync/__init__.py +5 -0
- openinterp/sync/r2.py +280 -0
- openinterp/syntax.py +399 -0
- openinterp/takedown_checks.py +76 -0
- openinterp/toxicity.py +158 -0
- openinterp/verdict_regression.py +79 -0
- openinterp/wiring_backfill.py +170 -0
- openinterp/wordnet.py +362 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Circuit pages: the registry's units joined by their wiring edges.
|
|
2
|
+
|
|
3
|
+
Every experiment's wiring (its own, or a backfilled row) names its unit's strongest
|
|
4
|
+
direct connections. An edge between two units that both have pages is kept. The units
|
|
5
|
+
that edges join form one circuit per connected group. Each circuit
|
|
6
|
+
is laid out by layer, so chains read left to right from earlier to later layers.
|
|
7
|
+
|
|
8
|
+
A *hub* is a unit wired to a large share of the model's studied units. An example is a
|
|
9
|
+
late neuron that every earlier unit's write moves a little. A hub would join them all
|
|
10
|
+
into one star-shaped circuit. So a hub's edges don't join circuits, and the page lists
|
|
11
|
+
hubs separately.
|
|
12
|
+
|
|
13
|
+
Each circuit and hub has its own page, named after one unit (:func:`unit_key`). A circuit
|
|
14
|
+
is named after its *root*, its first unit in layout order: the lowest layer, heads
|
|
15
|
+
first, then the lowest index. Circuits merge and split as the registry grows, so a root
|
|
16
|
+
can change. :mod:`openinterp.builder.circuit_pages` therefore writes a redirect at every
|
|
17
|
+
other unit's name. A URL from an earlier build then lands on the unit's current page.
|
|
18
|
+
|
|
19
|
+
:func:`wiring_view` is one experiment's wiring, with each edge's page linked.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from collections import defaultdict
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
|
|
27
|
+
from openinterp.builder.hrefs import circuit_href, hub_href
|
|
28
|
+
from openinterp.builder.tree import ModelNode, UnitNode, head_href, neuron_href
|
|
29
|
+
from openinterp.wiring_backfill import record_wiring
|
|
30
|
+
|
|
31
|
+
HREF = {"mlp_neuron": neuron_href, "attn_head": head_href}
|
|
32
|
+
COLUMN, ROW, PAD = 120, 44, 40
|
|
33
|
+
# A hub is wired to at least this share of the model's units with pages, and this many.
|
|
34
|
+
HUB_SHARE, HUB_MIN = 0.05, 10
|
|
35
|
+
HUB_RULE = {"hub_share": HUB_SHARE, "hub_min": HUB_MIN} # for the page
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def unit_key(unit: UnitNode) -> str:
|
|
39
|
+
"""The unit's name in circuit and hub URLs, from its page: ``L3-N7``, ``L4-tres-dc-F12``."""
|
|
40
|
+
within = unit.href.removesuffix(".html").split("/", 2)[2] # after models/<slug>/
|
|
41
|
+
return within.replace("/", "-")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class Reading:
|
|
46
|
+
"""One reading of one edge: which experiment's wiring found it, and how."""
|
|
47
|
+
|
|
48
|
+
unit: UnitNode # whose experiment it is
|
|
49
|
+
experiment: object # builder.tree.Experiment
|
|
50
|
+
method: str
|
|
51
|
+
weight: float
|
|
52
|
+
channels: dict[str, float]
|
|
53
|
+
backfilled: bool
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class CircuitEdge:
|
|
58
|
+
"""One connection between two units, with every reading that names it."""
|
|
59
|
+
|
|
60
|
+
source: UnitNode
|
|
61
|
+
target: UnitNode
|
|
62
|
+
readings: list[Reading] = field(default_factory=list)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def methods(self) -> list[str]:
|
|
66
|
+
"""The wiring methods that found this edge, sorted."""
|
|
67
|
+
return sorted({r.method for r in self.readings})
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class Circuit:
|
|
72
|
+
"""One connected group of units, laid out by layer."""
|
|
73
|
+
|
|
74
|
+
units: list[UnitNode]
|
|
75
|
+
edges: list[CircuitEdge]
|
|
76
|
+
positions: dict[str, tuple[int, int]] # href -> (x, y) of the node's centre
|
|
77
|
+
width: int
|
|
78
|
+
height: int
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def layers(self) -> tuple[int, int]:
|
|
82
|
+
"""The first and last layer of the circuit."""
|
|
83
|
+
return min(u.layer for u in self.units), max(u.layer for u in self.units)
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def root(self) -> UnitNode:
|
|
87
|
+
"""The unit the circuit is named after: its first in layout order."""
|
|
88
|
+
return self.units[0]
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def verified(self) -> int:
|
|
92
|
+
"""How many of the circuit's units stand VERIFIED."""
|
|
93
|
+
return sum(u.verified for u in self.units)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def circuit_edges(model: ModelNode, rows: dict) -> list[CircuitEdge]:
|
|
97
|
+
"""Return the edges between two of ``model``'s units with pages.
|
|
98
|
+
|
|
99
|
+
They come from every experiment's wiring. Each is found once per reading that names it.
|
|
100
|
+
"""
|
|
101
|
+
units = {u.href: u for layer in model.layers for u in layer.units}
|
|
102
|
+
edges: dict[tuple[str, str], CircuitEdge] = {}
|
|
103
|
+
for unit in units.values():
|
|
104
|
+
for e in unit.experiments:
|
|
105
|
+
weights, activations, backfilled = record_wiring(e.record, rows)
|
|
106
|
+
for wiring in (weights, activations):
|
|
107
|
+
if wiring is None:
|
|
108
|
+
continue
|
|
109
|
+
sides = [
|
|
110
|
+
(wiring.upstream_neurons, True),
|
|
111
|
+
(wiring.upstream_heads or [], True),
|
|
112
|
+
(wiring.downstream_neurons, False),
|
|
113
|
+
(wiring.downstream_heads or [], False),
|
|
114
|
+
]
|
|
115
|
+
for found, upstream in sides:
|
|
116
|
+
for edge in found:
|
|
117
|
+
other = units.get(HREF[edge.kind](model.slug, edge.layer, edge.index))
|
|
118
|
+
if other is None or other is unit:
|
|
119
|
+
continue
|
|
120
|
+
source, target = (other, unit) if upstream else (unit, other)
|
|
121
|
+
key = (source.href, target.href)
|
|
122
|
+
if key not in edges:
|
|
123
|
+
edges[key] = CircuitEdge(source, target)
|
|
124
|
+
edges[key].readings.append(
|
|
125
|
+
Reading(unit, e, wiring.method, edge.weight, edge.channels, backfilled)
|
|
126
|
+
)
|
|
127
|
+
return sorted(
|
|
128
|
+
edges.values(), key=lambda c: (c.source.layer, c.target.layer, c.source.href, c.target.href)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass
|
|
133
|
+
class Hub:
|
|
134
|
+
"""A unit wired to so many others that its edges don't join circuits."""
|
|
135
|
+
|
|
136
|
+
unit: UnitNode
|
|
137
|
+
edges: list[CircuitEdge] # every edge touching it, in layer order
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def neighbours(self) -> list[UnitNode]:
|
|
141
|
+
"""The units wired to the hub, in layer order, neurons before heads."""
|
|
142
|
+
found = {}
|
|
143
|
+
for e in self.edges:
|
|
144
|
+
other = e.target if e.source is self.unit else e.source
|
|
145
|
+
found[other.href] = other
|
|
146
|
+
return sorted(found.values(), key=lambda u: (u.layer, not u.is_head, u.idx))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def hubs(model: ModelNode, edges: list[CircuitEdge]) -> list[Hub]:
|
|
150
|
+
"""Return the units wired to at least ``HUB_SHARE`` of ``model``'s units with pages.
|
|
151
|
+
|
|
152
|
+
A hub also needs at least ``HUB_MIN`` edges. Most connected first.
|
|
153
|
+
"""
|
|
154
|
+
n_units = sum(len(layer.units) for layer in model.layers)
|
|
155
|
+
bar = max(HUB_MIN, HUB_SHARE * n_units)
|
|
156
|
+
touching: dict[str, list[CircuitEdge]] = defaultdict(list)
|
|
157
|
+
units = {}
|
|
158
|
+
for e in edges:
|
|
159
|
+
for u in (e.source, e.target):
|
|
160
|
+
touching[u.href].append(e)
|
|
161
|
+
units[u.href] = u
|
|
162
|
+
found = [
|
|
163
|
+
Hub(units[h], es)
|
|
164
|
+
for h, es in touching.items()
|
|
165
|
+
if len({o.href for e in es for o in (e.source, e.target)} - {h}) >= bar
|
|
166
|
+
]
|
|
167
|
+
return sorted(found, key=lambda hub: (-len(hub.neighbours), hub.unit.href))
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def circuits(model: ModelNode, rows: dict) -> list[Circuit]:
|
|
171
|
+
"""``model``'s circuits, largest first: groups of units joined by wiring edges."""
|
|
172
|
+
return circuits_and_hubs(model, rows)[0]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def circuits_and_hubs(model: ModelNode, rows: dict) -> tuple[list[Circuit], list[Hub]]:
|
|
176
|
+
"""Return ``model``'s circuits, largest first, and its hubs.
|
|
177
|
+
|
|
178
|
+
The circuits are joined by the edges that don't touch a hub.
|
|
179
|
+
"""
|
|
180
|
+
edges = circuit_edges(model, rows)
|
|
181
|
+
found_hubs = hubs(model, edges)
|
|
182
|
+
hub_hrefs = {hub.unit.href for hub in found_hubs}
|
|
183
|
+
edges = [e for e in edges if not {e.source.href, e.target.href} & hub_hrefs]
|
|
184
|
+
parent: dict[str, str] = {}
|
|
185
|
+
|
|
186
|
+
def find(h: str) -> str:
|
|
187
|
+
while parent.setdefault(h, h) != h:
|
|
188
|
+
parent[h] = parent[parent[h]]
|
|
189
|
+
h = parent[h]
|
|
190
|
+
return h
|
|
191
|
+
|
|
192
|
+
for edge in edges:
|
|
193
|
+
parent[find(edge.source.href)] = find(edge.target.href)
|
|
194
|
+
groups: dict[str, list[CircuitEdge]] = defaultdict(list)
|
|
195
|
+
for edge in edges:
|
|
196
|
+
groups[find(edge.source.href)].append(edge)
|
|
197
|
+
found = [_layout(group) for group in groups.values()]
|
|
198
|
+
return sorted(found, key=lambda c: (-len(c.units), c.layers, c.units[0].href)), found_hubs
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _layout(edges: list[CircuitEdge]) -> Circuit:
|
|
202
|
+
"""Columns by layer (heads before neurons, as attention runs first), rows within."""
|
|
203
|
+
units = {u.href: u for e in edges for u in (e.source, e.target)}
|
|
204
|
+
ordered = sorted(units.values(), key=lambda u: (u.layer, not u.is_head, u.idx, u.href))
|
|
205
|
+
columns: dict[int, list[UnitNode]] = defaultdict(list)
|
|
206
|
+
for u in ordered:
|
|
207
|
+
columns[u.layer].append(u)
|
|
208
|
+
positions = {}
|
|
209
|
+
for col, layer in enumerate(sorted(columns)):
|
|
210
|
+
for row, u in enumerate(columns[layer]):
|
|
211
|
+
positions[u.href] = (PAD + col * COLUMN, PAD + row * ROW)
|
|
212
|
+
width = 2 * PAD + (len(columns) - 1) * COLUMN
|
|
213
|
+
height = 2 * PAD + (max(len(c) for c in columns.values()) - 1) * ROW
|
|
214
|
+
return Circuit(ordered, edges, positions, width, height)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def unit_pages(slug: str, found: list[Circuit], found_hubs: list[Hub]) -> dict[str, str]:
|
|
218
|
+
"""Return the page each unit appears on, by unit href.
|
|
219
|
+
|
|
220
|
+
That is its circuit's page, else its own hub page, else the first hub it is wired to.
|
|
221
|
+
"""
|
|
222
|
+
pages = {}
|
|
223
|
+
for hub in found_hubs:
|
|
224
|
+
for u in hub.neighbours:
|
|
225
|
+
pages.setdefault(u.href, hub_href(slug, unit_key(hub.unit)))
|
|
226
|
+
for hub in found_hubs:
|
|
227
|
+
pages[hub.unit.href] = hub_href(slug, unit_key(hub.unit))
|
|
228
|
+
for c in found:
|
|
229
|
+
for u in c.units:
|
|
230
|
+
pages[u.href] = circuit_href(slug, unit_key(c.root))
|
|
231
|
+
return pages
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def wiring_view(wiring, slug: str, studied: set[str]) -> dict | None:
|
|
235
|
+
"""A wiring's edge lists with each edge's page linked when the registry has one."""
|
|
236
|
+
if wiring is None:
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
def rows(edges):
|
|
240
|
+
if edges is None:
|
|
241
|
+
return None
|
|
242
|
+
out = []
|
|
243
|
+
for edge in edges:
|
|
244
|
+
page = HREF[edge.kind](slug, edge.layer, edge.index)
|
|
245
|
+
# The channel the weight came from, when there are several to choose from.
|
|
246
|
+
via = max(edge.channels, key=lambda c: abs(edge.channels[c]), default=None)
|
|
247
|
+
out.append(
|
|
248
|
+
{
|
|
249
|
+
"e": edge,
|
|
250
|
+
"href": page if page in studied else None,
|
|
251
|
+
"via": via if len(edge.channels) > 1 else None,
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
return out
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
"w": wiring,
|
|
258
|
+
"groups": [
|
|
259
|
+
("Upstream neurons", rows(wiring.upstream_neurons)),
|
|
260
|
+
("Upstream heads", rows(wiring.upstream_heads)),
|
|
261
|
+
("Downstream neurons", rows(wiring.downstream_neurons)),
|
|
262
|
+
("Downstream heads", rows(wiring.downstream_heads)),
|
|
263
|
+
],
|
|
264
|
+
}
|