Path: blob/master/sage/quadratic_forms/quadratic_form__neighbors.py
4101 views
"""1Neighbors2"""3from sage.modules.free_module_element import vector4from sage.rings.integer_ring import ZZ5from copy import deepcopy6from sage.quadratic_forms.extras import extend_to_primitive7from sage.matrix.constructor import matrix8#from sage.quadratic_forms.quadratic_form import QuadraticForm ## This creates a circular import! =(91011####################################################################################12## Routines used for understanding p-neighbors, and computing classes in a genus. ##13####################################################################################141516def find_primitive_p_divisible_vector__random(self, p):17"""18Finds a random `p`-primitive vector in `L/pL` whose value is `p`-divisible.1920.. note::2122Since there are about `p^{(n-2)}` of these lines, we have a `1/p`23chance of randomly finding an appropriate vector.2425.. warning::2627If there are local obstructions for this to happen, then this algorithm28will never terminate... =( We should check for this too!2930EXAMPLES::3132sage: Q = QuadraticForm(ZZ, 2, [10,1,4])33sage: Q.find_primitive_p_divisible_vector__random(5) # random34(1, 1)35sage: Q.find_primitive_p_divisible_vector__random(5) # random36(1, 0)37sage: Q.find_primitive_p_divisible_vector__random(5) # random38(2, 0)39sage: Q.find_primitive_p_divisible_vector__random(5) # random40(2, 2)41sage: Q.find_primitive_p_divisible_vector__random(5) # random42(3, 3)43sage: Q.find_primitive_p_divisible_vector__random(5) # random44(3, 3)45sage: Q.find_primitive_p_divisible_vector__random(5) # random46(2, 0)4748"""49n = self.dim()50v = vector([ZZ.random_element(p) for i in range(n)])5152## Repeatedly choose random vectors, and evaluate until the value is p-divisible.53while True:54if (self(v) % p == 0) and (v != 0):55return v56else:57v[ZZ.random_element(n)] = ZZ.random_element(p) ## Replace a random entry and try again.5859606162#def find_primitive_p_divisible_vector__all(self, p):63# """64# Finds all random p-primitive vectors (up to scaling) in L/pL whose65# value is p-divisible.66#67# Note: Since there are about p^(n-2) of these lines, we should avoid this for large n.68# """69# pass707172def find_primitive_p_divisible_vector__next(self, p, v=None):73"""74Finds the next `p`-primitive vector (up to scaling) in `L/pL` whose75value is `p`-divisible, where the last vector returned was `v`. For76an intial call, no `v` needs to be passed.7778Returns vectors whose last non-zero entry is normalized to 0 or 1 (so no79lines are counted repeatedly). The ordering is by increasing the80first non-normalized entry. If we have tested all (lines of)81vectors, then return None.8283OUTPUT:84vector or None8586EXAMPLES::8788sage: Q = QuadraticForm(ZZ, 2, [10,1,4])89sage: v = Q.find_primitive_p_divisible_vector__next(5); v90(1, 1)91sage: v = Q.find_primitive_p_divisible_vector__next(5, v); v92(1, 0)93sage: v = Q.find_primitive_p_divisible_vector__next(5, v); v949596"""97## Initialize98n = self.dim()99if v == None:100w = vector([ZZ(0) for i in range(n-1)] + [ZZ(1)])101else:102w = deepcopy(v)103104105## Handle n = 1 separately.106if n <= 1:107raise RuntimeError, "Sorry -- Not implemented yet!"108109110## Look for the last non-zero entry (which must be 1)111nz = n - 1112while w[nz] == 0:113nz += -1114115## Test that the last non-zero entry is 1 (to detect tampering).116if w[nz] != 1:117print "Warning: The input vector to QuadraticForm.find_primitive_p_divisible_vector__next() is not normalized properly."118119120121## Look for the next vector, until w == 0122while True:123124125## Look for the first non-maximal (non-normalized) entry126ind = 0127while (ind < nz) and (w[ind] == p-1):128ind += 1129130#print ind, nz, w131132## Increment133if (ind < nz):134w[ind] += 1135for j in range(ind):136w[j] = 0137else:138for j in range(ind+1): ## Clear all entries139w[j] = 0140141if nz != 0: ## Move the non-zero normalized index over by one, or return the zero vector142w[nz-1] = 1143nz += -1144145146## Test for zero vector147if w == 0:148return None149150## Test for p-divisibility151if (self(w) % p == 0):152return w153154155156157## ----------------------------------------------------------------------------------------------158159def find_p_neighbor_from_vec(self, p, v):160"""161Finds the `p`-neighbor of this quadratic form associated to a given162vector `v` satisfying:163164#. `Q(v) = 0 \pmod p`165#. `v` is a non-singular point of the conic `Q(v) = 0 \pmod p`.166167Reference: Gonzalo Tornaria's Thesis, Thrm 3.5, p34.168169EXAMPLES::170171sage: Q = DiagonalQuadraticForm(ZZ,[1,1,1,1])172sage: v = vector([0,2,1,1])173sage: X = Q.find_p_neighbor_from_vec(3,v); X174Quadratic form in 4 variables over Integer Ring with coefficients:175[ 3 10 0 -4 ]176[ * 9 0 -6 ]177[ * * 1 0 ]178[ * * * 2 ]179180"""181R = self.base_ring()182n = self.dim()183B2 = self.matrix()184185## Find a (dual) vector w with B(v,w) != 0 (mod p)186v_dual = B2 * vector(v) ## We want the dot product with this to not be divisible by 2*p.187y_ind = 0188while ((y_ind < n) and (v_dual[y_ind] % p) == 0): ## Check the dot product for the std basis vectors!189y_ind += 1190if y_ind == n:191raise RuntimeError, "Oops! One of the standard basis vectors should have worked."192w = vector([R(i == y_ind) for i in range(n)])193vw_prod = (v * self.matrix()).dot_product(w)194195## DIAGNOSTIC196#if vw_prod == 0:197# print "v = ", v198# print "v_dual = ", v_dual199# print "v_dual[y_ind] = ", v_dual[y_ind]200# print "(v_dual[y_ind] % p) = ", (v_dual[y_ind] % p)201# print "w = ", w202# print "p = ", p203# print "vw_prod = ", vw_prod204# raise RuntimeError, "ERROR: Why is vw_prod = 0?"205206## DIAGNOSTIC207#print "v = ", v208#print "w = ", w209#print "vw_prod = ", vw_prod210211212## Lift the vector v to a vector v1 s.t. Q(v1) = 0 (mod p^2)213s = self(v)214if (s % p**2 != 0):215al = (-s / (p * vw_prod)) % p216v1 = v + p * al * w217v1w_prod = (v1 * self.matrix()).dot_product(w)218else:219v1 = v220v1w_prod = vw_prod221222## DIAGNOSTIC223#if (s % p**2 != 0):224# print "al = ", al225#print "v1 = ", v1226#print "v1w_prod = ", v1w_prod227228229## Construct a special p-divisible basis to use for the p-neighbor switch230good_basis = extend_to_primitive([v1, w])231for i in range(2,n):232ith_prod = (good_basis[i] * self.matrix()).dot_product(v)233c = (ith_prod / v1w_prod) % p234good_basis[i] = good_basis[i] - c * w ## Ensures that this extension has <v_i, v> = 0 (mod p)235236## DIAGNOSTIC237#print "original good_basis = ", good_basis238239## Perform the p-neighbor switch240good_basis[0] = vector([x/p for x in good_basis[0]]) ## Divide v1 by p241good_basis[1] = good_basis[1] * p ## Multiply w by p242243## Return the associated quadratic form244M = matrix(good_basis)245new_Q = deepcopy(self) ## Note: This avoids a circular import of QuadraticForm!246new_Q.__init__(R, M * self.matrix() * M.transpose())247return new_Q248return QuadraticForm(R, M * self.matrix() * M.transpose())249250251## ----------------------------------------------------------------------------------------------252253254#def find_classes_in_genus(self):255256257258259