Skip to content

Commit 1a2e145

Browse files
committed
feat(example): 3D visualization of the lifted dimension
1 parent 3ad0141 commit 1a2e145

11 files changed

Lines changed: 2867 additions & 1020 deletions

File tree

‎examples/graph_2d/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ construction cost in milliseconds.
103103
The colour key under the plot is itself a control: clicking an entry withdraws that reference graph's colour
104104
and the edges it claimed fall through to the next graph holding them, which is how the nesting gets peeled
105105
apart one layer at a time. `DEG only` names the remainder and is deliberately not a switch — the
106-
`theory colours` checkbox owns that flag on its own.
106+
`edge colors` checkbox owns that flag on its own.
107107

108108
| Action | Key |
109109
|---|---|
@@ -112,7 +112,7 @@ apart one layer at a time. `DEG only` names the remainder and is deliberately no
112112
| Release the selection or the start node | `Esc` |
113113
| Next graph | `v` |
114114
| New sample from the same distribution | `r` |
115-
| Overlays: coordinate cross, edges, vertex ids, theory colours, query markers | `g` / `e` / `i` / `c` / `q` |
115+
| Overlays: coordinate cross, edges, vertex ids, edge colors, query markers | `g` / `e` / `i` / `c` / `q` |
116116
| Zoom at the cursor / pan by dragging / restore the framing | scroll / drag / `0` |
117117
| Control sheet | `h` or `?` |
118118

‎examples/graph_2d/conftest.py‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Shared fixtures and test helpers for the graph_2d test suite."""
2+
3+
from __future__ import annotations
4+
5+
import matplotlib
6+
7+
matplotlib.use("Agg")
8+
9+
from functools import partial
10+
11+
import pytest
12+
from deg_graph import DEFAULT_K
13+
from deglib.distances import Metric
14+
from main import build_scene
15+
from matplotlib.backend_bases import KeyEvent
16+
from viewer import (
17+
DEG_VIEW,
18+
FLAG_START,
19+
GraphViewer,
20+
)
21+
22+
BUILD_START = ("blobs", 240, DEFAULT_K, 3, Metric.FP32_L2)
23+
24+
25+
@pytest.fixture(scope="module")
26+
def window():
27+
instance = GraphViewer(partial(build_scene, threads=1), *BUILD_START)
28+
instance._fig.canvas.draw_idle = lambda *_args, **_keywords: None
29+
yield instance
30+
instance.close()
31+
32+
33+
@pytest.fixture()
34+
def viewer(window: GraphViewer) -> GraphViewer:
35+
stale = (window.preset, window.num_points, window.k, window.seed, window.metric) != BUILD_START
36+
window.preset, window.num_points, window.k, window.seed, window.metric = BUILD_START
37+
window.selected = window.entry = window.hovered = None
38+
window.free_query = None
39+
window.query = window.status = None
40+
window._home = None
41+
window._drag = None
42+
window.view = DEG_VIEW
43+
window.search_metric = BUILD_START[4]
44+
window.top_k = 1
45+
window.overlays = window._held_graphs()
46+
window._held_seen = set(window.overlays)
47+
window._knng_edges = None
48+
for position, wanted in enumerate(FLAG_START):
49+
checks = window._checks1 if position < 3 else window._checks2
50+
local = position if position < 3 else position - 3
51+
if checks.get_status()[local] != wanted:
52+
window._toggle_flag(position)
53+
if window.show_3d:
54+
window.show_3d = False
55+
window._set_3d_visible(False)
56+
window._cube_3d.reset_limits()
57+
if stale:
58+
window._rebuild()
59+
else:
60+
window.entry = window.model.farthest_from_centroid()
61+
window.redraw()
62+
return window
63+
64+
65+
@pytest.fixture(scope="module")
66+
def mips_window():
67+
instance = GraphViewer(partial(build_scene, threads=1), *BUILD_START, mips=True)
68+
instance._fig.canvas.draw_idle = lambda *_args, **_keywords: None
69+
yield instance
70+
instance.close()
71+
72+
73+
@pytest.fixture()
74+
def mips_viewer(mips_window: GraphViewer) -> GraphViewer:
75+
stale = (
76+
mips_window.preset,
77+
mips_window.num_points,
78+
mips_window.k,
79+
mips_window.seed,
80+
mips_window.metric,
81+
mips_window.mips,
82+
) != (*BUILD_START, True)
83+
mips_window.preset, mips_window.num_points, mips_window.k, mips_window.seed, mips_window.metric = BUILD_START
84+
mips_window.mips = True
85+
mips_window.selected = mips_window.entry = mips_window.hovered = None
86+
mips_window.free_query = None
87+
mips_window.query = mips_window.status = None
88+
mips_window._home = None
89+
mips_window._drag = None
90+
mips_window.view = DEG_VIEW
91+
mips_window.search_metric = BUILD_START[4]
92+
mips_window.top_k = 1
93+
mips_window.overlays = mips_window._held_graphs()
94+
mips_window._held_seen = set(mips_window.overlays)
95+
mips_window._knng_edges = None
96+
for position, wanted in enumerate(FLAG_START):
97+
checks = mips_window._checks1 if position < 3 else mips_window._checks2
98+
local = position if position < 3 else position - 3
99+
if checks.get_status()[local] != wanted:
100+
mips_window._toggle_flag(position)
101+
if mips_window.show_3d:
102+
mips_window.show_3d = False
103+
mips_window._set_3d_visible(False)
104+
mips_window._cube_3d.reset_limits()
105+
if stale:
106+
mips_window._rebuild()
107+
else:
108+
mips_window.redraw()
109+
return mips_window
110+
111+
112+
def _key(viewer: GraphViewer, key: str) -> KeyEvent:
113+
return KeyEvent("key_press_event", viewer._fig.canvas, key)

‎examples/graph_2d/deg_graph.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from deglib.distances import FloatSpace, Metric
1212
from deglib.optimization import mips_l2_transform_query
1313
from pysearch import epsilon_search
14-
from theory import TheoryReport, compare, dissimilarities, knng_edges, nsw_edges
14+
from theory import TheoryReport, compare, knng_edges, nsw_edges
1515

1616
__all__ = [
1717
"DEFAULT_K",

‎examples/graph_2d/main.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ def build_scene(
5050
"""
5151
points, groups = make_points(preset, num_points, seed)
5252
features = mips_l2_transform(points)[0] if mips else points
53-
return build_model(
54-
features, groups, k=k, seed=seed, threads=threads, metric=metric, plot_points=points, mips=mips
55-
)
53+
return build_model(features, groups, k=k, seed=seed, threads=threads, metric=metric, plot_points=points, mips=mips)
5654

5755

5856
def main() -> None:

‎examples/graph_2d/pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ requires = ["setuptools>=61.0"]
1919
build-backend = "setuptools.build_meta"
2020

2121
[tool.setuptools]
22-
py-modules = ["main", "dataset", "deg_graph", "theory", "viewer"]
22+
py-modules = ["main", "dataset", "deg_graph", "theory", "viewer", "viewer_3d", "viewer_controls", "viewer_panel"]
2323

2424
[tool.uv.sources]
2525
deglib = { path = "../../python", editable = true }

0 commit comments

Comments
 (0)