| i = [x != 0 for x in v[0]].index(True) | i = [x != 0 for x in v[0]].index(True) | ||||
| return i, v[0, i] | return i, v[0, i] | ||||
| def xgcd_of_list(a): | |||||
| (g, s, t) = xgcd(a[0], a[1]) | |||||
| bezouts = [s, t] | |||||
| for v in a[2:]: | |||||
| (g, s_, t_) = xgcd(g, v) | |||||
| bezouts = [b*s_ for b in bezouts] | |||||
| bezouts.append(t_) | |||||
| return (g, bezouts) | |||||
| def remove_linear_dependencies(B, dim=None): | def remove_linear_dependencies(B, dim=None): | ||||
| nrows = B.nrows() | nrows = B.nrows() | ||||
| if dim is None or nrows > dim: | if dim is None or nrows > dim: | ||||
| r = K.dimensions()[0] | r = K.dimensions()[0] | ||||
| else: | else: | ||||
| r = nrows-dim | r = nrows-dim | ||||
| if r == 1: | |||||
| if r == 1 and False: | |||||
| print("Use Better Algo") | |||||
| # Find a linear dependency | # Find a linear dependency | ||||
| if K is None: | if K is None: | ||||
| K = B.left_kernel().basis_matrix() | K = B.left_kernel().basis_matrix() | ||||
| assert K.dimensions()[0] == 1 | assert K.dimensions()[0] == 1 | ||||
| combinaison = K[0] | combinaison = K[0] | ||||
| # Detect the redundant vector | |||||
| combinaison *= lcm([v.denominator() for v in combinaison]) | |||||
| print(combinaison) | |||||
| pivot, pivot_value = None, None | pivot, pivot_value = None, None | ||||
| for ind, value in enumerate(combinaison): | for ind, value in enumerate(combinaison): | ||||
| if abs(value) > 0 and (pivot is None or abs(value)<abs(pivot_value)): | |||||
| if abs(value) == 1: | |||||
| pivot, pivot_value = ind, value | pivot, pivot_value = ind, value | ||||
| break | |||||
| if pivot_value is None: | |||||
| print('Complex case') | |||||
| for ind, value in enumerate(combinaison): | |||||
| if abs(value) > 0 and gcd([v for i,v in enumerate(combinaison) if i!=ind]) == 1: | |||||
| if pivot is None or abs(value)<abs(pivot_value): | |||||
| pivot, pivot_value = ind, value | |||||
| if pivot_value < 0: | |||||
| combinaison = vector(ZZ, [-v for v in combinaison]) | |||||
| _, bezouts = xgcd_of_list([v for i,v in enumerate(combinaison) if i!=pivot]) | |||||
| factor = combinaison[pivot]-1 | |||||
| for i in range(len(combinaison)): | |||||
| ind = i if i < pivot else i-1 | |||||
| if i != pivot: | |||||
| B[i] += factor*bezouts[ind]*B[pivot] | |||||
| combinaison[pivot] += -factor | |||||
| assert (combinaison*B).is_zero(), 'It is not a linear dependency anymore !' | |||||
| assert abs(combinaison[pivot]) == 1, f'Error abs(combinaison[pivot]) == {abs(combinaison[pivot])} != 1' | |||||
| B = B[[i for i in range(B.dimensions()[0]) if i != pivot]] | B = B[[i for i in range(B.dimensions()[0]) if i != pivot]] | ||||
| else: | else: | ||||
| B = B.LLL() | B = B.LLL() | ||||
| B = B[r:] | B = B[r:] | ||||
| # Eliminate linear dependencies | # Eliminate linear dependencies | ||||
| if maintains_basis: | if maintains_basis: | ||||
| D = remove_linear_dependencies(D) | D = remove_linear_dependencies(D) | ||||
| # Go back to the primal | # Go back to the primal | ||||
| return D | return D | ||||
| def lattice_project_against(B, V, maintains_basis=True): | def lattice_project_against(B, V, maintains_basis=True): | ||||
| """ | """ | ||||
| Compute the projection of the lattice L(B) orthogonally to Span(V). All vectors if V | Compute the projection of the lattice L(B) orthogonally to Span(V). All vectors if V | ||||
| (or at least their projection on Span(B)) must belong to L(B). | |||||
| (or at least their projection on Span(B)) must belong to L(B). | |||||
| Algorithm: | Algorithm: | ||||
| - project V onto Span(B) | - project V onto Span(B) | ||||
| - project the basis onto orth(V) | - project the basis onto orth(V) | ||||
| def logdet(M, exact=False): | def logdet(M, exact=False): | ||||
| """ | |||||
| """ | |||||
| Compute the log of the determinant of a large rational matrix, | Compute the log of the determinant of a large rational matrix, | ||||
| tryping to avoid overflows. | tryping to avoid overflows. | ||||
| """ | """ | ||||
| def degen_inverse(S, B=None): | def degen_inverse(S, B=None): | ||||
| """ Compute the inverse of a symmetric matrix restricted | |||||
| """ Compute the inverse of a symmetric matrix restricted | |||||
| to its span | to its span | ||||
| """ | """ | ||||
| # Get an orthogonal basis for the Span of B | # Get an orthogonal basis for the Span of B | ||||
| def build_standard_substitution_matrix(V, pivot=None, data=None, output_data=False): | def build_standard_substitution_matrix(V, pivot=None, data=None, output_data=False): | ||||
| _, pivot = V.nonzero_positions()[0] if (pivot is None) else (None, pivot) | |||||
| _, pivot = V.nonzero_positions()[0] if (pivot is None) else (None, pivot) | |||||
| assert V[0,pivot] != 0, 'The value of the pivot must be non-zero.' | assert V[0,pivot] != 0, 'The value of the pivot must be non-zero.' | ||||
| dim = V.ncols() | dim = V.ncols() | ||||
| V1 = - V[0,:pivot] / V[0,pivot] | V1 = - V[0,:pivot] / V[0,pivot] | ||||
| V2 = - V[0,pivot+1:] / V[0,pivot] | V2 = - V[0,pivot+1:] / V[0,pivot] | ||||
| Gamma = zero_matrix(QQ, dim,dim-1) | Gamma = zero_matrix(QQ, dim,dim-1) | ||||
| Gamma[:pivot,:pivot] = identity_matrix(pivot) | Gamma[:pivot,:pivot] = identity_matrix(pivot) | ||||
| Gamma[pivot,:pivot] = V1 | Gamma[pivot,:pivot] = V1 | ||||
| normalization_matrix[pivot:,pivot:] = identity_matrix(dim-pivot-1) - V2.T*V2 / norm2 | normalization_matrix[pivot:,pivot:] = identity_matrix(dim-pivot-1) - V2.T*V2 / norm2 | ||||
| normalization_matrix[:pivot,pivot:] = - V1.T*V2 / norm2 | normalization_matrix[:pivot,pivot:] = - V1.T*V2 / norm2 | ||||
| normalization_matrix[pivot:,:pivot] = - V2.T*V1 / norm2 | normalization_matrix[pivot:,:pivot] = - V2.T*V1 / norm2 | ||||
| data['det'] = norm2 | data['det'] = norm2 | ||||
| data['normalization_matrix'] = normalization_matrix | data['normalization_matrix'] = normalization_matrix | ||||
| if output_data: | if output_data: | ||||
| return Gamma, data | return Gamma, data | ||||
| else: | else: | ||||
| return Gamma | |||||
| return Gamma |