Skip to content

Commit f7b76e9

Browse files
Added method for reducing a Seifert matrix to a non-singular one
1 parent ec04619 commit f7b76e9

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

dev/signature.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,87 @@ def basic_knot_test():
145145
assert K.signature() == values[m]
146146

147147

148+
def invertible_representative(seifert_matrix):
149+
"""
150+
For many calculations in the algebraic concordance group, one
151+
needs to replace the initial Seifert matrix with an invertible
152+
matrix that represents the same class. See::
153+
154+
Levine, Invariants of knot cobordism. Invent Math 8, 98–110 (1969).
155+
https://doi.org/10.1007/BF01404613
156+
157+
or the summary in::
158+
159+
Livingston, The algebraic concordance order of a knot.
160+
https://arxiv.org/abs/0806.3068
161+
162+
To keep the sizes of the entries of the final matrix under
163+
control, we use LLL at various points. We also use a "block
164+
matrix" form of Levine's lemma to reduce the amount of recursion.
165+
166+
Warning: this code has not been extensively tested.
167+
"""
168+
V = seifert_matrix
169+
n = V.nrows()
170+
assert V.base_ring() == ZZ
171+
assert (V - V.transpose()).det().abs() == 1
172+
assert (V + V.transpose()).rank() == n
173+
174+
# Start with the sublattice L that is left-orthogonal to
175+
# everything, i.e. the v with v*V = 0.
176+
177+
L = V.left_kernel_matrix()
178+
k = L.nrows()
179+
if k == 0:
180+
return V
181+
L = L.LLL() # The basis is the rows.
182+
183+
# Now look at the left-orthogonal complement of L, that is,
184+
#
185+
# U = {u with u * V * v = 0 for all v in L}
186+
187+
U = matrix(V * L.transpose()).left_kernel_matrix()
188+
assert U * V * L.transpose() == 0
189+
190+
# L is a submodule of U, and we now find a basis of U that extends
191+
# the one for L.
192+
193+
L_in_U = U.solve_left(L).change_ring(ZZ)
194+
E = extend_to_primitive(L_in_U).change_ring(ZZ)
195+
assert E.det().abs() == 1
196+
197+
# Another basis for U, this time with the basis for L for the
198+
# first vectors
199+
200+
U = E * U
201+
assert U[:k, :] == L
202+
# cleanup
203+
U = L.stack(U[k: , :].LLL())
204+
205+
# Extend U to a Z-basis of Z^n where the new vectors give the
206+
# standard dual basis of Hom(L, Z) via a -> (x -> a*V*x).
207+
208+
R = extend_to_primitive(U).change_ring(ZZ)
209+
A = R[U.nrows(): , :].LLL()
210+
F = A * V * L.transpose()
211+
assert F.det().abs() == 1
212+
A = F.inverse().change_ring(ZZ) * A
213+
214+
C = L.stack(A).stack(U[k: , :])
215+
assert C.det().abs() == 1
216+
217+
# We apply the block matrix generalization of the lemma in Levine's
218+
# Inventiones 1969 paper on page 101, to see that B is equivalent
219+
# in G^Q and hence G^Z. The block version follows from applying the
220+
# original repeatedly.
221+
222+
W = C * V * C.transpose()
223+
assert W[:k, :] == 0 and W[k:2*k, 0:k] == 1 and W[2*k: , :k] == 0
224+
B = W[2*k:, 2*k:]
225+
assert (B - B.transpose()).det().abs() == 1
226+
return invertible_representative(B)
227+
228+
148229
if __name__ == '__main__':
149230
K = spherogram.Link('K12n123')
150231
V = matrix(ZZ, K.seifert_matrix())

0 commit comments

Comments
 (0)