Skip to content

Commit bd6c08d

Browse files
Jamie Nuchoclaude
andcommitted
Show scatter plots for all 6 measurement spaces
Judge spaces use MDS projection from pairwise similarity matrices. All 6 tabs now have consistent layout: scatter plot + metrics + color-by toggle. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 07e3e60 commit bd6c08d

1 file changed

Lines changed: 66 additions & 82 deletions

File tree

web/src/components/PathInvariance.jsx

Lines changed: 66 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -217,94 +217,78 @@ export default function PathInvariance() {
217217
</button>
218218
))}
219219
</div>
220-
{!isJudge && (
221-
<div className="flex gap-2 ml-auto">
222-
<button
223-
onClick={() => setColorBy('phase')}
224-
className={`px-3 py-1 text-xs rounded border ${
225-
colorBy === 'phase' ? 'border-accent text-accent' : 'border-border text-muted'
226-
}`}
227-
>
228-
Color by Phase
229-
</button>
230-
<button
231-
onClick={() => setColorBy('model')}
232-
className={`px-3 py-1 text-xs rounded border ${
233-
colorBy === 'model' ? 'border-accent text-accent' : 'border-border text-muted'
234-
}`}
235-
>
236-
Color by Model
237-
</button>
238-
</div>
239-
)}
220+
<div className="flex gap-2 ml-auto">
221+
<button
222+
onClick={() => setColorBy('phase')}
223+
className={`px-3 py-1 text-xs rounded border ${
224+
colorBy === 'phase' ? 'border-accent text-accent' : 'border-border text-muted'
225+
}`}
226+
>
227+
Color by Phase
228+
</button>
229+
<button
230+
onClick={() => setColorBy('model')}
231+
className={`px-3 py-1 text-xs rounded border ${
232+
colorBy === 'model' ? 'border-accent text-accent' : 'border-border text-muted'
233+
}`}
234+
>
235+
Color by Model
236+
</button>
237+
</div>
240238
</div>
241239

242-
{/* Main layout */}
243-
<div className={`grid grid-cols-1 ${isJudge ? 'lg:grid-cols-3' : 'lg:grid-cols-5'} gap-6 mb-10`}>
244-
{/* Scatter plot — embedding spaces only */}
245-
{!isJudge && (
246-
<div className="lg:col-span-3">
247-
<ScatterPlot
248-
points={points}
249-
responses={responses}
250-
colorBy={colorBy}
251-
space={currentSpace}
252-
hoveredPoint={hoveredPoint}
253-
setHoveredPoint={setHoveredPoint}
254-
/>
255-
{/* Legend */}
256-
<div className="flex flex-wrap gap-3 mt-3">
257-
{colorBy === 'model'
258-
? Object.entries(MODEL_COLORS)
259-
.filter(([m]) => responses.some(r => r.model === m))
260-
.map(([m, c]) => (
261-
<div key={m} className="flex items-center gap-1.5">
262-
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: c }} />
263-
<span className="text-xs text-muted">{m}</span>
264-
</div>
265-
))
266-
: Object.entries(PHASE_COLORS)
267-
.filter(([p]) => responses.some(r => r.phase === p))
268-
.map(([p, c]) => (
269-
<div key={p} className="flex items-center gap-1.5">
270-
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: c }} />
271-
<span className="text-xs text-muted">{p}</span>
272-
</div>
273-
))
274-
}
275-
</div>
276-
{/* Hover info */}
277-
{hovered && (
278-
<div className="mt-3 p-3 border border-border rounded bg-panel text-xs">
279-
<span className="text-gray-200 font-medium">Q{hovered.question_num}: {hovered.question_title}</span>
280-
<span className="text-muted">{hovered.model}{hovered.phase}</span>
281-
</div>
282-
)}
283-
<p className="text-xs text-muted/60 mt-2">
284-
PCA projection of {space?.dimensions}d embeddings to 2D.
285-
{colorBy === 'phase'
286-
? ' Tight clusters = same phase, same endpoint. Switch to "Color by Model" — if clusters break apart, it\'s training bias. If they hold, it\'s structural.'
287-
: ' If model colors are scattered (no model-specific clusters), model identity doesn\'t predict position in embedding space.'}
288-
</p>
240+
{/* Main layout: scatter + metrics */}
241+
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6 mb-10">
242+
{/* Scatter plot */}
243+
<div className="lg:col-span-3">
244+
<ScatterPlot
245+
points={points}
246+
responses={responses}
247+
colorBy={colorBy}
248+
space={currentSpace}
249+
hoveredPoint={hoveredPoint}
250+
setHoveredPoint={setHoveredPoint}
251+
/>
252+
{/* Legend */}
253+
<div className="flex flex-wrap gap-3 mt-3">
254+
{colorBy === 'model'
255+
? Object.entries(MODEL_COLORS)
256+
.filter(([m]) => responses.some(r => r.model === m))
257+
.map(([m, c]) => (
258+
<div key={m} className="flex items-center gap-1.5">
259+
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: c }} />
260+
<span className="text-xs text-muted">{m}</span>
261+
</div>
262+
))
263+
: Object.entries(PHASE_COLORS)
264+
.filter(([p]) => responses.some(r => r.phase === p))
265+
.map(([p, c]) => (
266+
<div key={p} className="flex items-center gap-1.5">
267+
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: c }} />
268+
<span className="text-xs text-muted">{p}</span>
269+
</div>
270+
))
271+
}
289272
</div>
290-
)}
291-
292-
{/* Judge space description */}
293-
{isJudge && (
294-
<div className="lg:col-span-1">
295-
<div className="p-4 border border-border rounded-lg bg-panel">
296-
<p className="text-xs text-gray-200 font-medium mb-2">Pairwise LLM Judgment</p>
297-
<p className="text-xs text-muted">
298-
{space.judge_model} scored {space.dimensions === 'judgment' ? '663' : ''} response pairs for semantic similarity.
299-
No scatter plot — pairwise scores don't have spatial positions.
300-
The metrics are computed from the scored pairs directly.
301-
</p>
273+
{/* Hover info */}
274+
{hovered && (
275+
<div className="mt-3 p-3 border border-border rounded bg-panel text-xs">
276+
<span className="text-gray-200 font-medium">Q{hovered.question_num}: {hovered.question_title}</span>
277+
<span className="text-muted">{hovered.model}{hovered.phase}</span>
302278
</div>
303-
</div>
304-
)}
279+
)}
280+
<p className="text-xs text-muted/60 mt-2">
281+
{isJudge
282+
? `MDS projection from pairwise similarity scores (${space?.judge_model} as judge). Tight clusters = responses to the same question scored as highly similar.`
283+
: `PCA projection of ${space?.dimensions}d embeddings to 2D.`}
284+
{colorBy === 'phase'
285+
? ' Tight clusters = same phase, same endpoint. Switch to "Color by Model" — if clusters break apart, it\'s training bias. If they hold, it\'s structural.'
286+
: ' If model colors are scattered (no model-specific clusters), model identity doesn\'t predict position in embedding space.'}
287+
</p>
288+
</div>
305289

306290
{/* Metrics */}
307-
<div className={isJudge ? 'lg:col-span-2' : 'lg:col-span-2'}>
291+
<div className="lg:col-span-2">
308292
<div className="p-4 border border-border rounded-lg bg-panel mb-4">
309293
<h3 className="text-xs font-semibold text-accent mb-3 tracking-wide">
310294
KNN Purity (k=5)

0 commit comments

Comments
 (0)