From 95d63c78d3507a8d256ca4ff2ed113206370eef3 Mon Sep 17 00:00:00 2001 From: Chad-Peterson <97306485+Chad-Peterson@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:26:36 -0600 Subject: [PATCH] Implementing R3 --- examples/scratch/reidemeister_moves.py | 180 +----------------- .../spatial_graph_diagrams/Reidemeister.py | 110 ++++++++++- 2 files changed, 118 insertions(+), 172 deletions(-) diff --git a/examples/scratch/reidemeister_moves.py b/examples/scratch/reidemeister_moves.py index e7d8331..d2057bd 100644 --- a/examples/scratch/reidemeister_moves.py +++ b/examples/scratch/reidemeister_moves.py @@ -56,60 +56,6 @@ def pre_r3(): return sgd -def post_r3(): - - x0 = Crossing('x0') - x1 = Crossing('x1') - x2 = Crossing('x2') - x3 = Crossing('x3') - x4 = Crossing('x4') - - e0 = Edge('e0') - e1 = Edge('e1') - e2 = Edge('e2') - e3 = Edge('e3') - e4 = Edge('e4') - e5 = Edge('e5') - e6 = Edge('e6') - e7 = Edge('e7') - e8 = Edge('e8') - e9 = Edge('e9') - er1 = Edge('er1') - er2 = Edge('er2') - - x0[0] = er1[0] - x0[1] = er2[0] - x0[2] = e2[0] - x0[3] = e1[0] - - x1[0] = e4[1] - x1[1] = e0[1] - x1[2] = e5[0] - x1[3] = e8[0] - - x2[0] = e5[1] - x2[1] = e0[0] - x2[2] = e6[1] - x2[3] = er1[1] - - x3[0] = e7[1] - x3[1] = er2[1] - x3[2] = e6[0] - x3[3] = e3[0] - - x4[0] = e4[0] - x4[1] = e9[0] - x4[2] = e7[0] - x4[3] = e3[1] - - e1[1] = e8[1] - e2[1] = e9[1] - - - sgd = SpatialGraphDiagram([x0, x1, x2, x3, x4, e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, er1, er2]) - - return sgd - def post_r3_corrected(): x0 = Crossing('x0') @@ -159,7 +105,6 @@ def post_r3_corrected(): e1[1] = e8[1] e2[1] = e9[1] - sgd = SpatialGraphDiagram([x0, x1, x2, x3, x4, e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, er1, er2]) return sgd @@ -171,135 +116,30 @@ def post_r3_corrected(): pre_r3_has_r3, candidate_faces, candidate_faces_edges = has_r3(sgd) print('Has R3?', pre_r3_has_r3) +# Hard-coded demo +demo_edge = 'e6' +demo_crossings = ['x0', 'x2', 'x3'] -def find_common_edge(crossing1, crossing2): - for adjacent1 in crossing1.adjacent: - for adjacent2 in crossing2.adjacent: - if adjacent1[0] == adjacent2[0]: - return adjacent1[0] - - -def find_opposite_edge(crossing, face): - for entrypoint in face: - if isinstance(entrypoint.vertex, Edge): - crossing_adjacent = [adjacent[0] for adjacent in crossing.adjacent] - if entrypoint.vertex not in crossing_adjacent: - return entrypoint.vertex - - -def get_index_of_crossing_corner(crossing, corner, opposite_side=False): - for i in range(4): - if crossing.adjacent[i][0] == corner: - if not opposite_side: - return i - else: - return (i + 2) % 4 - raise Exception('Corner not found in crossing') - - -def get_crossing_shift_indices(keep_crossing, remove_crossing1, remove_crossing2): - edge1 = find_common_edge(keep_crossing, remove_crossing1) - edge2 = find_common_edge(keep_crossing, remove_crossing2) - - keep_crossing_index_e1 = get_index_of_crossing_corner(keep_crossing, edge1) - keep_crossing_index_e2 = get_index_of_crossing_corner(keep_crossing, edge2) - - if keep_crossing_index_e1 == (keep_crossing_index_e2 - 1) % 4: - shifted_crossing_index_e1 = (keep_crossing_index_e1 - 1) % 4 - shifted_crossing_index_e2 = (keep_crossing_index_e2 + 1) % 4 - elif keep_crossing_index_e1 == (keep_crossing_index_e2 + 1) % 4: - shifted_crossing_index_e1 = (keep_crossing_index_e1 + 1) % 4 - shifted_crossing_index_e2 = (keep_crossing_index_e2 - 1) % 4 - else: - raise Exception('Edges are not adjacent') - - return shifted_crossing_index_e1, shifted_crossing_index_e2 - -# TODO Temporarily hardcode candidate face; double check this later. -# candidate_face = sgd.has_r3()[1] -# candidate_crossings = get_candidate_crossings(candidate_face) +# Find the face and edge objects for the demo faces = sgd.faces() -desired_crossings = ['x0', 'x2', 'x3'] -# candidate_face = [face for face in faces if all([entrypoint.vertex.label in desired_crossings for entrypoint in face])] - -# Find the face that has all 3 desired crossings for face in faces: entrypoints = [entrypoint.vertex.label for entrypoint in face] - if all([desired_crossing in entrypoints for desired_crossing in desired_crossings]): + if all([demo_crossing in entrypoints for demo_crossing in demo_crossings]): candidate_face = face break -candidate_crossings = get_candidate_crossings(candidate_face) - -# keep_crossing = candidate_crossings[0] -# remove_crossing_1 = candidate_crossings[1] -# remove_crossing_2 = candidate_crossings[2] -# TODO Remove hard-coding -keep_crossing = [crossing for crossing in candidate_crossings if crossing.label == 'x0'][0] -reidemeister_crossing_1 = [crossing for crossing in candidate_crossings if crossing.label == 'x2'][0] -reidemeister_crossing_2 = [crossing for crossing in candidate_crossings if crossing.label == 'x3'][0] - -# Define the Reidemeister crossing corners, and which are under/over -rc1_0_under = reidemeister_crossing_1.adjacent[0] -rc1_1_over = reidemeister_crossing_1.adjacent[1] -rc1_2_under = reidemeister_crossing_1.adjacent[2] -rc1_3_over = reidemeister_crossing_1.adjacent[3] -rc1_edges = [rc1_0_under, rc1_1_over, rc1_2_under, rc1_3_over] - - - -rc2_0_under = reidemeister_crossing_2.adjacent[0] -rc2_1_over = reidemeister_crossing_2.adjacent[1] -rc2_2_under = reidemeister_crossing_2.adjacent[2] -rc2_3_over = reidemeister_crossing_2.adjacent[3] -rc2_edges = [rc2_0_under, rc2_1_over, rc2_2_under, rc2_3_over] - -# Find the edges the keep and each remove crossing have in common -common_edge_1 = find_common_edge(keep_crossing, reidemeister_crossing_1) -common_edge_2 = find_common_edge(keep_crossing, reidemeister_crossing_2) -uncommon_edge = find_opposite_edge(keep_crossing, candidate_face) - -# Find the indices of common and uncommon edges -rc1_common_edge_index = get_index_of_crossing_corner(reidemeister_crossing_1, common_edge_1) -rc2_common_edge_index = get_index_of_crossing_corner(reidemeister_crossing_2, common_edge_2) -rc1_common_flipside_edge_index = get_index_of_crossing_corner(reidemeister_crossing_1, common_edge_1, opposite_side=True) -rc2_common_flipside_edge_index = get_index_of_crossing_corner(reidemeister_crossing_2, common_edge_2, opposite_side=True) - -# Fixme -# Fuse the edges of the keep crossing that are not being shifted by the Reidemeister move -sgd.remove_edge(common_edge_1) -sgd.remove_edge(common_edge_2) -kc_rc1_index = get_index_of_crossing_corner(keep_crossing, common_edge_1) -kc_rc2_index = get_index_of_crossing_corner(keep_crossing, common_edge_2) -keep_crossing[kc_rc1_index] = reidemeister_crossing_1.adjacent[rc1_common_flipside_edge_index] -keep_crossing[kc_rc2_index] = reidemeister_crossing_2.adjacent[rc2_common_flipside_edge_index] - -# Reassign the Reidemeister crossing edges +candidate_edge = [entrypoint.vertex for entrypoint in candidate_face if entrypoint.vertex.label == demo_edge][0] -shifted_index1, shifted_index2 = get_crossing_shift_indices(keep_crossing, reidemeister_crossing_1, reidemeister_crossing_2) +sgd_r3 = r3(sgd, candidate_face, candidate_edge) -rc1_new_edge, rc1_new_edge_index = keep_crossing.adjacent[shifted_index1] -rc2_new_edge, rc2_new_edge_index = keep_crossing.adjacent[shifted_index2] -reidemeister_crossing_1[rc1_common_edge_index] = rc1_new_edge[rc1_new_edge_index] -reidemeister_crossing_2[rc2_common_edge_index] = rc2_new_edge[rc2_new_edge_index] -# Add Edges? -er1 = Edge('er1') -er2 = Edge('er2') -sgd.add_edge(er1, - reidemeister_crossing_1, rc1_common_flipside_edge_index, - keep_crossing, shifted_index1) +# Reassign the Reidemeister crossing edges -sgd.add_edge(er2, - reidemeister_crossing_2, rc2_common_flipside_edge_index, - keep_crossing, shifted_index2) -# Is it necessary to merge the vertices? -# sgd._merge_vertices() -yp = sgd.normalized_yamada_polynomial() +yp = sgd_r3.normalized_yamada_polynomial() print('After R3:', yp) -print('Has R3?', has_r3(sgd)[0]) +print('Has R3?', has_r3(sgd_r3)[0]) diff --git a/src/yamada/spatial_graph_diagrams/Reidemeister.py b/src/yamada/spatial_graph_diagrams/Reidemeister.py index 37b26c7..203b428 100644 --- a/src/yamada/spatial_graph_diagrams/Reidemeister.py +++ b/src/yamada/spatial_graph_diagrams/Reidemeister.py @@ -78,6 +78,7 @@ def has_r3(sgd): faces = sgd.faces() candidate_faces = [] + candidate_faces_edges = [] for face in faces: @@ -138,9 +139,114 @@ def edge_is_over(edge, crossing): return False -def r3(): - pass +def r3(sgd, face, edge): + + # Make a copy of the sgd object + # TODO DOES THIS CAUSE ISSUES? + # sgd = sgd.copy() + + # Label the crossings + crossings = [entrypoint.vertex for entrypoint in face if isinstance(entrypoint.vertex, Crossing)] + reidemeister_crossing_1 = edge.adjacent[0][0] + reidemeister_crossing_2 = edge.adjacent[1][0] + keep_crossing = [crossing for crossing in crossings if crossing != reidemeister_crossing_1 and crossing != reidemeister_crossing_2][0] + + # Label the edges + common_edge_1 = find_common_edge(keep_crossing, reidemeister_crossing_1) + common_edge_2 = find_common_edge(keep_crossing, reidemeister_crossing_2) + uncommon_edge = edge + + # ... + + # Find the Reidemeister crossing indices of the common edges, and their continuations on the other sides + rc1_common_edge_index = get_index_of_crossing_corner(reidemeister_crossing_1, common_edge_1) + rc1_common_flipside_edge_index = get_index_of_crossing_corner(reidemeister_crossing_1, common_edge_1, + opposite_side=True) + + rc2_common_edge_index = get_index_of_crossing_corner(reidemeister_crossing_2, common_edge_2) + rc2_common_flipside_edge_index = get_index_of_crossing_corner(reidemeister_crossing_2, common_edge_2, + opposite_side=True) + + # ... + kc_rc1_index = get_index_of_crossing_corner(keep_crossing, common_edge_1) + kc_rc2_index = get_index_of_crossing_corner(keep_crossing, common_edge_2) + + # Remove the edges between the Reidemeister crossings and the keep crossing. + sgd.remove_edge(common_edge_1) + sgd.remove_edge(common_edge_2) + + # Connect the Reidemeister crossing flipside edges to the keep crossing + keep_crossing[kc_rc1_index] = reidemeister_crossing_1.adjacent[rc1_common_flipside_edge_index] + keep_crossing[kc_rc2_index] = reidemeister_crossing_2.adjacent[rc2_common_flipside_edge_index] + + # Find the shifted indices of the Reidemeister crossings + shifted_index1, shifted_index2 = get_crossing_shift_indices(keep_crossing, reidemeister_crossing_1, + reidemeister_crossing_2) + + rc1_new_edge, rc1_new_edge_index = keep_crossing.adjacent[shifted_index1] + rc2_new_edge, rc2_new_edge_index = keep_crossing.adjacent[shifted_index2] + + reidemeister_crossing_1[rc1_common_edge_index] = rc1_new_edge[rc1_new_edge_index] + reidemeister_crossing_2[rc2_common_edge_index] = rc2_new_edge[rc2_new_edge_index] + + # Add Edges? + # TODO Automate naming + er1 = Edge('er1') + er2 = Edge('er2') + + sgd.add_edge(er1, + reidemeister_crossing_1, rc1_common_flipside_edge_index, + keep_crossing, shifted_index1) + + sgd.add_edge(er2, + reidemeister_crossing_2, rc2_common_flipside_edge_index, + keep_crossing, shifted_index2) + + return sgd + + +def find_common_edge(crossing1, crossing2): + for adjacent1 in crossing1.adjacent: + for adjacent2 in crossing2.adjacent: + if adjacent1[0] == adjacent2[0]: + return adjacent1[0] + + +def find_opposite_edge(crossing, face): + for entrypoint in face: + if isinstance(entrypoint.vertex, Edge): + crossing_adjacent = [adjacent[0] for adjacent in crossing.adjacent] + if entrypoint.vertex not in crossing_adjacent: + return entrypoint.vertex + + +def get_index_of_crossing_corner(crossing, corner, opposite_side=False): + for i in range(4): + if crossing.adjacent[i][0] == corner: + if not opposite_side: + return i + else: + return (i + 2) % 4 + raise Exception('Corner not found in crossing') + + +def get_crossing_shift_indices(keep_crossing, remove_crossing1, remove_crossing2): + edge1 = find_common_edge(keep_crossing, remove_crossing1) + edge2 = find_common_edge(keep_crossing, remove_crossing2) + + keep_crossing_index_e1 = get_index_of_crossing_corner(keep_crossing, edge1) + keep_crossing_index_e2 = get_index_of_crossing_corner(keep_crossing, edge2) + + if keep_crossing_index_e1 == (keep_crossing_index_e2 - 1) % 4: + shifted_crossing_index_e1 = (keep_crossing_index_e1 - 1) % 4 + shifted_crossing_index_e2 = (keep_crossing_index_e2 + 1) % 4 + elif keep_crossing_index_e1 == (keep_crossing_index_e2 + 1) % 4: + shifted_crossing_index_e1 = (keep_crossing_index_e1 + 1) % 4 + shifted_crossing_index_e2 = (keep_crossing_index_e2 - 1) % 4 + else: + raise Exception('Edges are not adjacent') + return shifted_crossing_index_e1, shifted_crossing_index_e2 # %% Reidemeister 4