"""Test the user's proposed v_c rotation algorithm. Algorithm: 1) Find edge e_0 = (v_c, v_0) between depth-d and depth-(d-1) faces. 2) List edges incident to v_c in clockwise embedding order. 3) Edge-switch each in sequence until reaching an outer-cycle edge. Two implementations / interpretations to try: (A) clockwise from e_0 toward one side (whichever has fewer chord edges to traverse) (B) clockwise unconditionally, possibly going around v_c """ import sys, os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import math import networkx as nx from stress_test_termination import ( compute_depths, apply_switch, check_balanced, face_edges ) def clockwise_edges_at(v, faces, chords, outer_edges, n): """Return all edges incident to v in clockwise order (starting at angle 90 degrees and going to angle 0, -90, ...). Derives the edge set from the current faces, so it works after edge switches have changed the chord structure.""" incident = set() for f in faces: fe = face_edges(f) for e in fe: if v in e: incident.add(e) # Compute angle for each incident edge def angle(e): other = [u for u in e if u != v][0] # Position of `other` on the polygon a = (90 - other * 360 / n) % 360 return a # Get edges sorted by clockwise embedding (decreasing angle from v) # Actually since CW = decreasing angle, sort by -angle. return sorted(incident, key=lambda e: -angle(e)) def find_edge_d_dm1(faces, depth): d_max = max(depth.values()) for F_idx, F in enumerate(faces): if depth[F_idx] != d_max: continue for e in face_edges(F): others = [j for j in range(len(faces)) if j != F_idx and e in face_edges(faces[j])] if others and depth[others[0]] == d_max - 1: return F_idx, others[0], e return None def v_c_rotation_step(faces, chords, outer_edges, n, direction='cw'): """Apply the user's algorithm: find edge e_0 = (v_c, v_0), rotate around v_c in `direction` until hitting outer edge. Returns (new_faces, new_chords, switches_done). """ outer_set = {frozenset(e) for e in outer_edges} depth = compute_depths(faces, outer_set) if max(depth.values()) == 0: return faces, chords, [] res = find_edge_d_dm1(faces, depth) if res is None: return faces, chords, [] F_idx, Fp_idx, e0 = res F = faces[F_idx] Fp = faces[Fp_idx] # Pick v_c (and v_0 = the other endpoint) u, v = tuple(e0) # Try both choices of v_c and use the one that gives a shorter sequence best = None for v_c, v_0 in [(u, v), (v, u)]: cw = clockwise_edges_at(v_c, faces, chords, outer_edges, n) # Rotate cw so that e0 is first e0_fs = frozenset(e0) idx = cw.index(e0_fs) rotated_cw = cw[idx:] + cw[:idx] if direction == 'ccw': # Reverse direction (but keep e0 first) rotated_cw = [e0_fs] + list(reversed(cw[:idx] + cw[idx + 1:])) # Sequence: switch each until we hit an outer edge seq = [] for e in rotated_cw: if e in outer_set: break seq.append(e) if best is None or len(seq) < len(best[2]): best = (v_c, v_0, seq) v_c, v_0, seq = best switches = [] cur_faces, cur_chords = list(faces), list(chords) for e in seq: # Find face containing F that has e as one of its edges, then third vertex u, v = tuple(e) # Find the two faces sharing e sharing = [i for i, f in enumerate(cur_faces) if e in face_edges(f)] if len(sharing) != 2: print(f' edge {tuple(e)} has {len(sharing)} adjacent faces; skipping') break f1, f2 = cur_faces[sharing[0]], cur_faces[sharing[1]] w = [vert for vert in f1 if vert not in (u, v)][0] x = [vert for vert in f2 if vert not in (u, v)][0] if w == x: print(f' edge {tuple(e)} would create self-loop; skipping') break cur_faces = apply_switch(cur_faces, (u, v), (w, x)) switches.append((tuple(e), (w, x))) return cur_faces, cur_chords, switches def run_v_c_algorithm(faces, chords, outer_edges, n, max_rounds=20, verbose=True): outer_set = {frozenset(e) for e in outer_edges} cur = list(faces) cur_chords = list(chords) total = 0 for round_ in range(max_rounds): depth = compute_depths(cur, outer_set) d_max = max(depth.values()) if verbose: print(f'Round {round_}: max depth = {d_max}, ' f'#faces = {len(cur)}') if d_max == 0: print(f'TERMINATED in {round_} rounds, {total} total switches.') return cur, total new_cur, new_chords, switches = v_c_rotation_step( cur, cur_chords, outer_edges, n) if not switches: print(' No switches available; stuck.') return cur, total if verbose: print(f' did {len(switches)} switches: {switches[:3]}' f'{"..." if len(switches) > 3 else ""}') cur = new_cur cur_chords = new_chords total += len(switches) print(f'Hit max_rounds={max_rounds}, final max depth = ' f'{max(compute_depths(cur, outer_set).values())}') return cur, total if __name__ == '__main__': # 9-vertex example print('=== 9-vertex example ===') n9 = 9 outer9 = [(i, (i + 1) % n9) for i in range(n9)] chords9 = [(0, 2), (0, 3), (3, 5), (3, 6), (0, 6), (6, 8)] faces9 = [ (0, 1, 2), (0, 2, 3), (3, 4, 5), (3, 5, 6), (6, 7, 8), (6, 8, 0), (0, 3, 6), ] run_v_c_algorithm(faces9, chords9, outer9, n9) print('\n=== 24-vertex example ===') n24 = 24 def arm(a, b): return [ (a, a + 1, a + 2), (a, a + 2, b), (a + 2, a + 3, a + 4), (a + 2, a + 4, b), (a + 4, a + 5, a + 6), (a + 4, a + 6, b), (a + 6, a + 7, b), ] outer24 = [(i, (i + 1) % n24) for i in range(n24)] chords24 = [(0, 8), (8, 16), (0, 16)] faces24 = [(0, 8, 16)] for (a, b) in [(0, 8), (8, 16), (16, 24)]: fs = arm(a, b) fs = [tuple(0 if v == 24 else v for v in vt) for vt in fs] faces24.extend(fs) for c in [(a, a + 2), (a + 2, a + 4), (a + 2, b), (a + 4, a + 6), (a + 4, b), (a + 6, b)]: chords24.append(tuple(0 if v == 24 else v for v in c)) run_v_c_algorithm(faces24, chords24, outer24, n24)