@@ -387,8 +387,8 @@ def test_describe_covers_the_built_graph(model) -> None:
387387
388388 assert "vertices" in text and "build" in text and "4" in text
389389 overlap = [line for line in lines if line .startswith ("∩" )]
390- assert [line .split ()[1 ] for line in overlap ] == ["delaunay" , "gabriel" , "rng" , "mst" , "mrng" ], (
391- "one line per reference"
390+ assert [line .split ()[1 ] for line in overlap ] == ["knng" , "nsw" , " delaunay" , "gabriel" , "rng" , "mst" , "mrng" , "nsg " ], (
391+ "one line per reference, in the selector's order "
392392 )
393393 for line , item in zip (overlap , model .theory .graphs ):
394394 assert f"{ item .edges :6d} " in line and f"{ item .shared :7d} " in line , "the panel quotes the report"
@@ -1052,9 +1052,11 @@ def test_the_k_field_follows_the_view_it_lands_on(viewer: GraphViewer) -> None:
10521052
10531053 class _SpinStub :
10541054 state = "normal"
1055+ increment = 1
10551056
1056- def configure (self , state : str ) -> None :
1057+ def configure (self , state : str , increment : int = 1 ) -> None :
10571058 self .state = state
1059+ self .increment = increment
10581060
10591061 viewer ._k_spin = _SpinStub ()
10601062 try :
@@ -1073,6 +1075,31 @@ def configure(self, state: str) -> None:
10731075 viewer ._k_spin = None
10741076
10751077
1078+ def test_the_k_field_steps_by_two_only_for_the_deg (viewer : GraphViewer ) -> None :
1079+ """The DEG builds at an even degree, so its k arrows step by 2; the knng and NSW read k raw and step by 1."""
1080+
1081+ class _SpinStub :
1082+ state = "normal"
1083+ increment = 1
1084+
1085+ def configure (self , state : str , increment : int = 1 ) -> None :
1086+ self .state = state
1087+ self .increment = increment
1088+
1089+ viewer ._k_spin = _SpinStub ()
1090+ try :
1091+ viewer ._on_view (DEG_VIEW )
1092+ assert viewer ._k_spin .increment == 2 , "the DEG snaps k to even, so a step of 1 would leave the down arrow inert"
1093+
1094+ viewer ._on_view (KNNG_VIEW )
1095+ assert viewer ._k_spin .increment == 1 , "the knng reads the raw neighbour count, so it steps by 1"
1096+
1097+ viewer ._on_view (NSW_VIEW )
1098+ assert viewer ._k_spin .increment == 1 , "the NSW reads the raw k too"
1099+ finally :
1100+ viewer ._k_spin = None
1101+
1102+
10761103def test_viewer_picks_the_vertex_under_the_cursor (viewer : GraphViewer ) -> None :
10771104 viewer ._fig .canvas .draw ()
10781105 target = 42
@@ -1112,9 +1139,11 @@ def test_the_mrng_view_is_offered_undirected_and_timed(viewer: GraphViewer) -> N
11121139
11131140 class _SpinStub :
11141141 state = "normal"
1142+ increment = 1
11151143
1116- def configure (self , state : str ) -> None :
1144+ def configure (self , state : str , increment : int = 1 ) -> None :
11171145 self .state = state
1146+ self .increment = increment
11181147
11191148 viewer ._k_spin = _SpinStub ()
11201149 try :
@@ -1138,19 +1167,22 @@ def configure(self, state: str) -> None:
11381167 assert drawn <= symmetrised , "and that graph is a subgraph of the symmetrised NSG graph"
11391168
11401169
1141- def test_the_nsg_view_is_offered_directed_but_never_compared (viewer : GraphViewer ) -> None :
1170+ def test_the_nsg_view_is_offered_directed_and_scored_in_the_report (viewer : GraphViewer ) -> None :
11421171 """
11431172 The NSG is a viewer-built view like the knng and the NSW: offered under every metric, drawn directed,
1144- reads no k — yet it holds no report entry, so it is never scored against the DEG.
1173+ reads no k. It carries a report entry, so the panel scores it against the DEG — yet it stays out of
1174+ the colour key, so its own view states its edge count and time, never a DEG share.
11451175 """
11461176 assert NSG_VIEW in viewer .views , "the NSG is offered in the selector"
1147- assert NSG_VIEW not in viewer .model .theory .names , "but it is not a reference graph in the comparison "
1177+ assert NSG_VIEW in viewer .model .theory .names , "the NSG is scored against the DEG in the report "
11481178
11491179 class _SpinStub :
11501180 state = "normal"
1181+ increment = 1
11511182
1152- def configure (self , state : str ) -> None :
1183+ def configure (self , state : str , increment : int = 1 ) -> None :
11531184 self .state = state
1185+ self .increment = increment
11541186
11551187 viewer ._k_spin = _SpinStub ()
11561188 try :
@@ -1162,7 +1194,7 @@ def configure(self, state: str) -> None:
11621194 assert viewer ._view_directed (), "the NSG is drawn directed"
11631195 panel = viewer ._panel_text .get_text ()
11641196 assert panel .startswith ("nsg" ), "the column heads with the NSG's name"
1165- assert "of DEG" not in panel , "the NSG states no DEG share — it is not compared "
1197+ assert "of DEG" not in panel , "the NSG view states its own edge count and time, not a DEG share "
11661198
11671199 drawn = {(int (u ), int (v )) for u , v in viewer ._display_edges ()}
11681200 library = {
@@ -1499,7 +1531,7 @@ def test_compare_counts_the_deg_edges_each_graph_shares() -> None:
14991531
15001532 undirected = {tuple (sorted (edge )) for edge in scene .edges .reshape (- 1 , 2 )}
15011533 assert report .deg_edges == len (undirected )
1502- assert tuple (item .name for item in report .graphs ) == ("delaunay" , "gabriel" , "rng" , "mst" , "mrng" )
1534+ assert tuple (item .name for item in report .graphs ) == ("knng" , "nsw" , " delaunay" , "gabriel" , "rng" , "mst" , "mrng" , "nsg " )
15031535 assert all (0 < item .shared <= min (report .deg_edges , item .edges ) for item in report .graphs )
15041536
15051537 by_name = {item .name : item for item in report .graphs }
@@ -1631,8 +1663,8 @@ def test_an_inner_product_report_carries_only_the_graphs_it_can_decide() -> None
16311663 euclidean = build_scene ("blobs" , 120 , DEFAULT_K , 5 , Metric .FP32_L2 , threads = 1 ).theory
16321664 inner = build_scene ("blobs" , 120 , DEFAULT_K , 5 , Metric .FP32_InnerProduct , threads = 1 ).theory
16331665
1634- assert euclidean .names == ("delaunay" , "gabriel" , "rng" , "mst" , "mrng" )
1635- assert inner .names == ("rng" , "mst" , "mrng" )
1666+ assert euclidean .names == ("knng" , "nsw" , " delaunay" , "gabriel" , "rng" , "mst" , "mrng" , "nsg " )
1667+ assert inner .names == ("knng" , "nsw" , " rng" , "mst" , "mrng" , "nsg " )
16361668
16371669
16381670def test_the_colour_key_names_only_what_the_report_holds () -> None :
@@ -1831,7 +1863,7 @@ def test_viewer_reports_the_theory_overlap_on_every_build(viewer: GraphViewer) -
18311863
18321864 assert viewer .model .theory is not None , "a fresh cloud is compared again"
18331865 names = tuple (item .name for item in viewer .model .theory .graphs )
1834- assert len (names ) == 5 , "every reference graph is listed"
1866+ assert len (names ) == 8 , "every graph the selector offers is listed, the knng and NSW among them "
18351867 assert all (f"∩ { name } " in viewer ._panel_text .get_text () for name in names ), "the reseeded panel lists them all"
18361868 assert all (f"∩ { name } " in text for name in names ), "and so did the panel before the reseed"
18371869 assert all (item .shared > 0 for item in viewer .model .theory .graphs )
@@ -1925,13 +1957,13 @@ def test_an_overlay_switch_survives_a_rebuild(viewer: GraphViewer) -> None:
19251957
19261958 viewer ._on_metric ("IP" )
19271959
1928- assert viewer .overlays == {"mst" , "mrng" }, "the graphs that were on stay on, the hidden one stays off"
1960+ assert viewer .overlays == {"mst" , "mrng" , "nsg" , "knng" , "nsw" }, "the graphs that were on stay on, the hidden one stays off"
19291961 assert viewer ._legend_names () == ("DEG only" , "rng" , "mrng" , "mst" ), "and the key names exactly those"
19301962
19311963 viewer ._on_metric ("L2" )
19321964
19331965 assert "rng" not in viewer .overlays , "the graph is still hidden when the metric brings it back"
1934- assert viewer .overlays == {"delaunay" , "gabriel" , "mst" , "mrng" }
1966+ assert viewer .overlays == {"delaunay" , "gabriel" , "mst" , "mrng" , "nsg" , "knng" , "nsw" }
19351967
19361968
19371969def test_a_graph_new_to_the_report_defaults_on (viewer : GraphViewer ) -> None :
@@ -2117,16 +2149,20 @@ def test_viewer_draws_the_graph_the_selector_picks(viewer: GraphViewer) -> None:
21172149 DEG_VIEW ,
21182150 KNNG_VIEW ,
21192151 NSW_VIEW ,
2120- * (overlap .name for overlap in viewer .model .theory .graphs ),
2152+ "delaunay" ,
2153+ "gabriel" ,
2154+ "rng" ,
2155+ "mst" ,
2156+ "mrng" ,
21212157 NSG_VIEW ,
21222158 NONE_VIEW ,
2123- ), "the DEG, the knng, the NSW, the reference graphs , the NSG and the empty view are the selector's entries "
2159+ ), "the selector lists every graph in GRAPH_ORDER's order , the knng, NSW and NSG among them "
21242160
21252161 viewer ._fig .canvas .draw ()
21262162 assert len (viewer ._edges .get_segments ()) == viewer .model .edges .shape [0 ], "the DEG view draws the graph itself"
21272163 assert viewer ._legend .get_visible (), "and has to explain what its edge colours mean"
21282164
2129- for name in viewer .model .theory .names :
2165+ for name in ( n for n in OVERLAP_ORDER if n in viewer .model .theory .names ) :
21302166 viewer ._on_view (name )
21312167 viewer ._fig .canvas .draw ()
21322168
0 commit comments