@@ -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+
148229if __name__ == '__main__' :
149230 K = spherogram .Link ('K12n123' )
150231 V = matrix (ZZ , K .seifert_matrix ())
0 commit comments