Skip to content

Commit

Permalink
Implementing R3
Browse files Browse the repository at this point in the history
  • Loading branch information
Chad-Peterson committed Feb 20, 2024
1 parent 61a4efa commit 95d63c7
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 172 deletions.
180 changes: 10 additions & 170 deletions examples/scratch/reidemeister_moves.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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
Expand All @@ -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])

110 changes: 108 additions & 2 deletions src/yamada/spatial_graph_diagrams/Reidemeister.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def has_r3(sgd):

faces = sgd.faces()
candidate_faces = []

candidate_faces_edges = []

for face in faces:
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 95d63c7

Please sign in to comment.